剑指 Offer 51. 数组中的逆序对

· 约 2 分钟阅读 · 次阅读 leetcode

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

 

示例 1:

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


暴力解法

时间复杂度O(n^2), 无法通过OJ

class Solution {
public:
    int reversePairs(vector<int>& nums) {
        int n = nums.size();
        int ans = 0;
        for (int i = 0; i < n; i++) {
            for (int j = i+1; j < n; j++) {
                if (nums[j] < nums[i]) {
                    ans++;
                }
            }
        }
        return ans;

    }
};

归并排序

两个有序数组merge的时候, 当将要选右边数组合并到新数组时,被选中的这个数比左半数组中的数都要小, 此时逆序对数增加为左边数组中的长度。

时间复杂度O(nlogn) 空间复杂度O(n)

class Solution {
    int ans = 0;
public:
    int reversePairs(vector<int>& nums) {
        int n = nums.size();
        mergeSort(nums, 0, n-1);
        return ans;
    }

    void mergeSort(vector<int>& nums, int left, int right) {
        int n = nums.size();
        if (left >= right) {
            return;
        }
        int mid = left + (right-left)/2;
        mergeSort(nums, left, mid);
        mergeSort(nums, mid+1, right);
        //如果拆分后的两部分已经有序,则不用merge
        if (nums[mid] <= nums[mid+1]) {
            return;
        }
        merge(nums, left, mid, right);
    }

    //[left, mid], [mid+1, right]
    void merge(vector<int>& nums, int left, int mid, int right) {
        vector<int> tmp;
        int i = left, j = mid+1;
        while (i <= mid && j <= right) {
            if (nums[i] <= nums[j]) {
                tmp.emplace_back(nums[i]);
                i++;
            }else{
                ans += mid-i+1;
                tmp.emplace_back(nums[j]);
                j++;
            }
        }
        while (i <= mid) {
            tmp.emplace_back(nums[i]);
            i++;
        }
        while (j <= right) {
            tmp.emplace_back(nums[j]);
            j++;
        }
        for (int i = left, j=0; i <= right; i++,j++) {
            nums[i] = tmp[j];
        }
    }

    
};

临时数组仅申请一次,降低中间申请销毁的时间

class Solution {
    int ans = 0;
public:
    int reversePairs(vector<int>& nums) {
        int n = nums.size();
        vector<int> tmp(n);
        mergeSort(nums, tmp, 0, n-1);
        return ans;
    }

    void mergeSort(vector<int>& nums, vector<int>& tmp, int left, int right) {
        int n = nums.size();
        if (left >= right) {
            return;
        }
        int mid = left + (right-left)/2;
        mergeSort(nums, tmp, left, mid);
        mergeSort(nums, tmp, mid+1, right);
        //如果拆分后的两部分已经有序,则不用merge
        if (nums[mid] <= nums[mid+1]) {
            return;
        }
        merge(nums, tmp, left, mid, right);
    }

    //[left, mid], [mid+1, right]
    void merge(vector<int>& nums, vector<int>& tmp, int left, int mid, int right) {
        int i = left, j = mid+1, k=left;
        while (i <= mid && j <= right) {
            if (nums[i] <= nums[j]) {
                tmp[k++] = nums[i++];
            }else{
                ans += mid-i+1;
                tmp[k++] = nums[j++];
            }
        }
        while (i <= mid) {
            tmp[k++] = nums[i++];
        }
        while (j <= right) {
            tmp[k++] = nums[j++];
        }
        copy(tmp.begin()+left, tmp.begin()+right+1, nums.begin()+left);
        // for (int i = left,k=left; i <= right; i++,k++) {
        //     nums[i] = tmp[k];
        // }
    }
    
};

离散化树状数组

TODO