数组中的逆序对

题目描述

来源于 https://leetcode-cn.com/

在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数。

示例 1:

输入: [7,5,6,4]
输出: 5

限制:

0 <= 数组长度 <= 50000

解法:

这个题可真巧妙啊,利用归并排序巧妙地计算出了数组中的逆序对个数。

考虑下面的数组,数组被分为了两个有序的子数组,现在要对这两个子数组进行归并。

[                 ]
 1 3 4 5 | 2 5 6 7

[1                ]
   3 4 5 | 2 5 6 7

在归并 2 的时候,第一个子数组中还有 3 个未归并,因此 2 和左边子数组中余下的元素构成逆序对。归并 3 的时候 3 肯定是小于其后的所有元素的,因此不构成逆序对。

基于以上分析,只需要在归并右边的数组中的元素时,累加左边数组中余下的元素的数量,就可以得到上面这个数组的逆序对了。

左右两边的有序子数组,也是归并得到的。在归并得到它们的时候,可以计算出子数组中未排序时候的逆序对数量。

所以,在归并排序的 merge 阶段,合并右侧数组时候,累加左侧子数组元素的数量,就可以统计出整个数组的逆序对数量了。

class Solution {
public:
    int reversePairs(vector<int>& nums) {
        vector<int> aux(nums.size());
        int reverse_pairs_num = merge_sort(nums, aux, 0, nums.size());
        return reverse_pairs_num;
    }

private:
    static int merge_sort(vector<int>& nums, vector<int>& aux, int lo, int hi){
        if(hi - lo <= 1){
            return 0;
        }
        
        int reverse_pairs_num = 0;
        auto mid = lo + (hi - lo) / 2;

        reverse_pairs_num += merge_sort(nums, aux, lo, mid);
        reverse_pairs_num += merge_sort(nums, aux, mid, hi);
        reverse_pairs_num += merge(nums, aux, lo, mid, hi);

        return reverse_pairs_num;
    }

    static int merge(vector<int>& nums, vector<int>& aux, int lo, int mid, int hi){
        copy(nums.begin()+lo, nums.begin()+hi, aux.begin()+lo);
        
        auto i = lo, j = mid;
        int reverse_pairs_num = 0;

        for(int k=lo;k<hi;k++){
            if(i == mid){
                nums[k] = aux[j++];
            }else if(j == hi){
                nums[k] = aux[i++];
            }else if(aux[i] <= aux[j]){
                nums[k] = aux[i++];
            }else{
                nums[k] = aux[j++];
                reverse_pairs_num += mid - i;
            }
        }
        
        return reverse_pairs_num;
    }
};