【剑指Offer-51】数组中的逆序对

问题

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

示例

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

解答

class Solution {
public:
    int reversePairs(vector<int>& nums) {
        tmp.resize(nums.size());
        mergeSort(nums, 0, (int)nums.size() - 1);
        return res;
    }
private:
    vector<int> tmp;
    int res = 0;
    void mergeSort(vector<int>& nums, int left, int right) {
        if (left >= right) return;
        int mid = left + (right - left) / 2;
        mergeSort(nums, left, mid);
        mergeSort(nums, mid + 1, right);
        merge(nums, left, mid, right);
    }
    void merge(vector<int>& nums, int left, int mid, int right) {
        int i = left, j = mid + 1, cnt = left;;
        while (i <= mid && j <= right) {
            if (nums[i] <= nums[j]) tmp[cnt++] = nums[i++];
            else {
                res += mid - i + 1; // 与归并排序的区别
                tmp[cnt++] = nums[j++];
            }
        }
        while (i <= mid) tmp[cnt++] = nums[i++];
        while (j <= right) tmp[cnt++] = nums[j++];
        for (int i = left; i <=right; i++) nums[i] = tmp[i];
    }
}; 

重点思路

归并排序的详解参考【C++】排序算法。归并排序非常适合处理前后数字对之间大小关系的问题。对于两个有序数列,其中某一个数字满足要求时,该数列后续所有数都满足要求。本题更一般的写法可以将上述代码20~26行改为以下内容:

while (i <= mid && j <= right) { // 排序
    if (nums[i] <= nums[j]) tmp[cnt++] = nums[i++];
    else tmp[cnt++] = nums[j++];
}
int ii = left, jj = mid + 1;
while (ii <= mid && jj <= right) { // 题目要求
    if (nums[ii] <= nums[jj]) ii++;
    else {
        res += mid - ii + 1;
        jj++;
    }
}

这个写法先完成两个数组的排序操作,再完成本题的要求。但是很容易发现下面的while循环和上面的while循环的内外部条件语句都是相同的,所以可以合并,就得到了最上面的那份代码。

这个更一般的写法可以使用在【LeetCode-493】翻转对

posted @ 2021-03-05 22:05  tmpUser  阅读(38)  评论(0编辑  收藏  举报