You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用二叉索引树(BIT)统计数组中满足nums[i]>2*nums[j]的逆序对?BIT应用与数据离散化的概念性问询

如何使用二叉索引树(BIT)统计数组中满足nums[i]>2*nums[j]的逆序对?BIT应用与数据离散化的概念性问询

嘿,我来帮你把这个问题拆明白,咱们从BIT的核心作用说起,再聊离散化的必要性,尽量用通俗的逻辑讲清楚~

一、BIT怎么帮我们统计符合条件的逆序对?

首先得回忆下BIT(二叉索引树/ Fenwick树)的核心能力:它能快速做单点更新和前缀/后缀频率查询,这刚好戳中了逆序对统计的痛点——我们需要高效追踪已处理元素的分布,快速算出有多少元素满足某个大小条件。

咱们的目标是找所有i < j且nums[i] > 2*nums[j]的对,换个遍历思路就好理解了:

  • 我们从数组末尾往开头遍历(也就是先处理j更大的元素)
  • 每处理到一个元素nums[i]时,我们需要知道:已经被我们加入BIT的元素(也就是原数组中j > i的那些元素)里,有多少个满足nums[j] < nums[i]/2(因为nums[i] > 2*nums[j]等价于nums[j] < nums[i]/2)
  • 这个查询到的数量,就是以i为左端点的符合条件的逆序对数目,把所有查询结果累加起来就是总数
  • 查完之后,把当前的nums[i]插入BIT(也就是更新BIT中对应值的频率+1),让前面的元素(更靠左的i')能查询到它

举个小例子直观感受:
比如数组是[3,1,2],从后往前遍历:

  1. 先处理2:BIT是空的,查询<1的元素数量为0,累加0;把2加入BIT
  2. 再处理1:查询<0.5的元素数量为0,累加0;把1加入BIT
  3. 最后处理3:查询<1.5的元素数量为1(就是已经加入的1),累加1;把3加入BIT
    最终总数是1,对应逆序对(0,1)(3>2*1),完全正确。

BIT在这里的优势是把每次查询和更新的时间压缩到了O(log k)(k是离散化后的数值范围大小),整个算法的时间复杂度就是O(n log n),比暴力的O(n²)高效太多。

二、数据离散化在这里的作用是什么?

你肯定会疑惑:为啥要做离散化?直接用元素值当BIT的索引不行吗?
答案是:如果数组里的元素范围很大(比如到1e9),我们根本不可能创建一个大小为1e9的BIT——内存直接爆掉。而离散化的核心就是把大数值范围压缩成我们能处理的小范围,同时保留数值的相对大小关系(毕竟我们只需要比较大小,不需要用到数值本身的具体值)。

具体到这个问题,离散化的步骤是这样的:

  1. 收集所有需要比较的数值:因为我们的条件涉及nums[i]和2*nums[j]的比较,所以要把数组里的每个元素nums[x],以及每个元素的2倍值2*nums[x]都收集到一个列表里
  2. 排序去重:把这个列表排序,然后去掉重复值,得到一个有序的唯一值列表
  3. 映射索引:对于任意一个值v,用二分查找找到它在这个有序列表中的位置,这个位置就是它在BIT中的索引

为啥要包含2*nums[x]?因为我们的查询条件本质是比较nums[i]和2*nums[j]的大小,所以这些2倍值也是我们需要参与排序比较的对象,只有把它们都加入离散化的列表,才能保证我们的大小比较逻辑是正确的。

还是用刚才的[3,1,2]例子:

  • 收集的数值是3,6,1,2,2,4
  • 排序去重后是[1,2,3,4,6]
  • 每个值对应的索引(假设BIT从1开始计数):1→1,2→2,3→3,4→4,6→5
    这样BIT的大小只需要5,完全可以轻松处理。

总结一下

  • BIT的作用:高效维护已处理元素的频率分布,快速查询满足nums[j] < nums[i]/2的元素数量,避免暴力枚举的低效
  • 离散化的作用:把超大的数值范围压缩到BIT可处理的大小,同时保留数值的相对大小,保证比较逻辑的正确性

备注:内容来源于stack exchange,提问作者Vishal Jangid

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.20 13:04:39