算法复习之 树状数组 + 数组离散化求解逆序对

  1. 问题:在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数。
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;
    }
}
posted @ 2021-04-22 16:09  Cruel_King  阅读(57)  评论(0编辑  收藏  举报