1. 程式人生 > 實用技巧 >劍指 Offer 51. 陣列中的逆序對

劍指 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;
    }
};