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

劍指offer 51 - 陣列中的逆序對

題目

https://leetcode-cn.com/problems/shu-zu-zhong-de-ni-xu-dui-lcof

題意

在陣列中的兩個數字,如果前面一個數字大於後面的數字,則這兩個數字組成一個逆序對。輸入一個數組,求出這個陣列中的逆序對的總數。

示例 1:
輸入: [7,5,6,4]
輸出: 5

思路

author's blog == http://www.cnblogs.com/toulanboy/

基礎想法

(1)本題要求的是逆序對,逆序對的定義題面已經給出。
(2)而求逆序對的常見解法是藉助歸併排序來實現,更具體是藉助他的merge過程

歸併排序求逆序對

歸併邏輯

我們知道,歸併排序實際上分治的思想,歸併排序的邏輯如下:
(1)其先將陣列不斷細分,最後細分到每個陣列只有1個元素

,那麼這個只包含1個元素陣列中就是有序的。
(2)然後將兩個有序的長度為1的陣列依次合併為長度為2的有序陣列,後面合併過程就是依次類推,2合44合8,...,n/2合n

Merge過程

值得注意的是:在上述的合併過程中,我們是可以得到其逆序對個數的?

舉個例子來說明合併過程:
陣列A: 11 22 33 44
陣列B: 12 20 55 66
陣列A和陣列B合併為一個數組。
正常做法是:分別用2個指標p1, p2指向A和B的第一個元素。然後進行兩兩對比,誰小誰下來放進新陣列。

進一步地,我們可以利用這個合併過程。若在上述例子中,p1指向22, p2指向20。那麼可知p1 > p2,,們可知逆序對(22, 20),而且還可知道p1後面的數,都比20要大,所以可得到逆序對(33, 20)、(44, 20)。 故可得到的逆序對個數是p1後面剩餘的元素個數。

通過合併過程累加,就可以得到整個序列的逆序對。

可能有同學想問:上述合併,只是得到了兩個相對位置陣列的逆序對,但逆序對個數還應該包括這個位置相對其他位置的逆序對,這部分是如何統計上去的?

答:實際上,上述兩個相對位置數組合併為1個大陣列後,他必然會經歷和前面大陣列、後面大陣列的過程,所以他前面的元素、他後面的元素相對他的逆序對個數也會被統計到的。

程式碼

//author's blog == http://www.cnblogs.com/toulanboy/
class Solution {
public:
    int ans = 0;
    vector<int> temp;//輔助陣列,用來儲存新的有序陣列
    void fenzhi(vector<int>& nums, int left, int right){
        if(left >= right){
            return;
        }
        int mid = (left+right) >> 1;
        fenzhi(nums, left, mid);//拆分,得到左邊
        fenzhi(nums, mid+1, right);//拆分,得到右邊
        merge(nums, left, mid, right);//左右合併
    }
    void merge(vector<int>& nums, int left, int mid, int right){
        int p1 = left;
        int p2 = mid+1;
        temp.clear();
        while(p1 <= mid && p2 <= right){
            if(nums[p1] <= nums[p2]){
                temp.push_back(nums[p1]);
                p1++;
            }
            else{
                temp.push_back(nums[p2]);
                p2++;
                ans += (mid-p1+1);
            }
        }
        while(p1 <= mid){
            temp.push_back(nums[p1]);
            p1++;
        }
        while(p2 <= right){
            temp.push_back(nums[p2]);
            p2++;
        }
        for(int i=0; i<temp.size(); ++i){
            nums[left+i] = temp[i];
        }
    }

    int reversePairs(vector<int>& nums) {
        fenzhi(nums, 0, nums.size()-1);
        return ans;
    }
};