算法复习之 树状数组 + 数组离散化求解逆序对
- 问题:在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数。
class Solution {
int lowBit(int x) {
return x & (-x);
}
int sum(int[] c, int x) {
int ret = 0;
while(x > 0) {
ret += c[x];
x -= lowBit(x);
}
return ret;
}
public void add(int[] c, int n, int x, int d) {
while(x <= n) {
c[x] += d;
x += lowBit(x);
}
}
public int reversePairs(int[] nums) {
/*
使用树状数组可以求解逆序对问题。对于数组中的某个数字i,对他的处理方式是,首先将树状数组中下表i的值 + 1,表示树状数组中目前存储的i元素多了一个。然后统计比他小的数字在他后面的个数(也即查询目前树状数组中前(i - 1)项的和。
上述我们使用树状数组中的下标对应了数组中的数值i。由于不知道数组的最大值和最小值,我们选用离散化将数字离散到(1 ~ m)上,其中m的值为数组中不重复元素的个数。
*/
Set <Integer> set = new TreeSet<>();
for(int i = 0; i < nums.length; i ++) {
set.add(nums[i]);
} // 将数组中的值排序并且去重
int idx = 1;
Map<Integer, Integer> mp = new HashMap<>();
for(Integer v : set) {
mp.put(v, idx ++);
} // 使用map结构实现离散化,将值v离散化为idx
int ans = 0;
int[] c = new int[idx];
for(int i = nums.length - 1; i >= 0 ; i --) {
int temp = mp.get(nums[i]); // 对于每个数字,获取到他在离散化后数组中的值
add(c, idx - 1, temp, 1); // 对这个较小的,但是在数组中大小顺序不变的数字进行树状数组的操作
ans += sum(c, temp - 1);
}
return ans;
}
}
时间并不会因为你的迷茫和迟疑而停留,就在你看这篇文章的同时,不知道有多少人在冥思苦想,在为算法废寝忘食,不知道有多少人在狂热地拍着代码,不知道又有多少提交一遍又一遍地刷新着OJ的status页面……
没有谁生来就是神牛,而千里之行,始于足下!