树状数组通用模版
class BIT {
int n; //这个是外部传进来数组的大小
int[] c; //这个是预处理的树状数组
public BIT(int n) {
this.n = n;
c = new int[n + 1]; //树状数组一定要开n+1,从1开始,不然的话 ,add操作 index = index+lowbit(index),index为0就造成了死循环
}
public void add(int index, int val) {
//默认模版是,除了更新它自己 还要更新它的父亲。一直更新下去,但是碰到逆序对这种,只要饿更新它自己即可
while (index <= n) {
c[index] += val;
index += lowbit(index);
}
}
//查询原数组 0 ~ index之内的和
public int query(int index) {
int res = 0;
while (index > 0) {
res += c[index];
index -= lowbit(index);
}
return res;
}
//如果要查询给定区间内的和 比如 i~j 区间内的和,就是 query[j] - query[i-1]
public int querySum(int from, int to) {
return query(to) - query(from - 1);
}
private int lowbit(int x) {
return x & (-x);
}
}
利用树状数组求逆序对,具体思路写在注释中了
/**
* 树状数组写法
* 根据题意分析,i < j 且 nums[i] > 2*nums[j],这个条件我们可以解读为,当前元素后方有多少元素比这个元素的一半还小
* 推荐博客
* https://blog.csdn.net/gonganDV/article/details/88817416、
* https://blog.csdn.net/S_999999/article/details/99076076
*
* 简要思路:
* 我们知道逆序对的定义是 i < j 且 a[i] > a[j]
*【为什么要用树状数组】
* 当数据的范围较小时,比如 下标最大为len,那么我们可以开一个数组c[len],来记录前面数据的出现情况,初始化为0;
* 当数据a出现时,就令c[a]=1。
* 这样的话,欲求某个数a的逆序数,只需要算出在当前状态下c[a+1,len]中有多少个1,因为这些位置的数在a之前出现且比a大。
* 但是若每添加一个数据a时,就得从a+1到 len搜一遍,复杂度太高了
*
*【怎么用树状数组】
* 用树状数组能够很好的解决这个问题。把数按照数组中的顺序一个个插入到树状数组中,每插入一个数,就统计比它小的数的个数
* 对应的逆序为 i - query(c[i]),其中 i 为当前已经插入的数的个数
* query(c[i])为比 c[i] 小的数的个数
* i- query(c[i]) 即比c[i] 大的个数, 即逆序的个数。
* 最后需要把所有逆序数求和,就是在插入的过程中边插入边求和.
*
*【怎么写】
* 构建一个值的范围是1~n的BIT,按照j = 0,1,2,···,n-1的顺序进行如下操作。
* 1、BIT c[j] 位置加上 1
* 2、res += j - query(c[i])
*
*【更进一步】
* 当数组长度较大时,直接开数组显然是不行了,这是的解决办法就是离散化。
* 假如现在有一些数:1234 98756 123456 99999 56782,由于1234是第一小的数,所以num[1]=1;
* 依此,有 num[5] = 2, num[2] = 3, num[4] = 4, num[3] = 5; 这样转化后并不影响原来数据的相对大小关系
*
*/
class BIT{
int n;
int[] c;
public BIT(int n) {
this.n = n;
c = new int[n + 1];
}
public void add(int i, int val) {
while (i <= n) {
c[i] += val;
i += i & -i;
}
}
public int query(int i) {
int res = 0;
while (i > 0) {
res += c[i];
i -= i & (-i);
}
return res;
}
}
public int reversePairs(int[] nums) {
//离散化
int n = nums.length;
if (n == 0) return 0;
int[] arr = Arrays.stream(nums).distinct().sorted().toArray();
Map<Integer, Integer> map = new HashMap<>();
for (int i = 0; i < arr.length; i++) {
map.put(arr[i], i + 1);
}
int res = 0;
BIT bit = new BIT(n);
for (int i = 1; i <= nums.length; i++) {
bit.add(map.get(nums[i - 1]), 1);
res += i - bit.query(map.get(nums[i - 1]));
}
return res;
}