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

题目描述

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

示例1:

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

限制:

0 <= 数组长度 <= 50000

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/shu-zu-zhong-de-ni-xu-dui-lcof

思路解析

假设给定数组为[8, 10, 15, 20, 70, 9, 25, 50, 60, 100],这个数组的前一半和后一半都是有序的,那么此时这个数组的逆序对的个数应该如何统计?
指针i指向[8, 10, 15, 20, 70]首元素,指针j指向[9, 25, 50, 60, 100]首元素,比较var(i)var(j)的大小。(这里var(i)var(j)ij指向元素的值)

  • var(i) < = var(j),则此时ij指向元素不构成逆序对,否则构成逆序对(j++);
  • var(i) < = var(j),则代表后半段的数组中,有j个元素小于var(i);(这里假设ij均从0开始)
  • 由此可统计出前半段的数组中每个元素所构成的逆序对的个数,且后半段数组中不存在逆序对。

使用归并排序的思想,当元素分组至每组仅有一个元素时,代表该组已经有序,返回此有序数组中逆序对的个数为0,随后进行归并,并记录每次归并所产生的数组的逆序对的个数。
时间复杂度\(O(n \log n)\),空间复杂度\(O(n)\)

代码实现

class Solution {
private:
    vector<int> orderedArray;   // From large to small
public:
    int reversePairs(vector<int>& nums) {
        int n = nums.size();
        vector<int> tmp(n);
        return mergeSort(nums, tmp, 0, n - 1);
    }
    int mergeSort(vector<int>& nums, vector<int>& tmp, int left, int right) {
        if(left >= right) 
            return 0;
        int mid = (left + right) / 2;
        int prev_count = mergeSort(nums, tmp, left, mid) + mergeSort(nums, tmp, mid + 1, right);
        // Merge
        int i = left;
        int j = mid + 1;
        int curr_count = 0;
        int tmp_index = left;
        while(i <= mid && j <= right) {
            if(nums[i] <= nums[j]) {
                tmp[tmp_index] = nums[i++];
                curr_count += (j - mid - 1);
            }
            else{
                tmp[tmp_index] = nums[j++];
            }
            tmp_index++;
        }
        while(i <= mid){
            tmp[tmp_index++] = nums[i++];
            curr_count += (j - mid - 1);
        }
        while(j <= right)
            tmp[tmp_index++] = nums[j++];
        for(tmp_index = left; tmp_index <= right; tmp_index++)
            nums[tmp_index] = tmp[tmp_index];
        return prev_count + curr_count;
    }
};
posted @ 2020-09-18 21:24  行者橙子  阅读(131)  评论(0编辑  收藏  举报