1. 程式人生 > >歸並排序 之 逆序對

歸並排序 之 逆序對

-- 組成 public 排序。 size 做到 int cnblogs 進行

1. 歸並排序

技術分享圖片

要點:

歸並排序是建立在歸並操作的一種有效的算法,該算法是采用 分治法 的典型應用。

基本思想:

(1)分解:將序列每次折半劃分成兩個數組,直到劃分成每個元素一個數組

(2)合並:將劃分後的序列段兩兩合並後排序。

2.逆數對問題

在數組中的兩個數字,如果前面一個數字大於後面的數字,則這兩個數字組成一個逆序對。輸入一個數組,求出這個數組中的逆序對的總數P。並將P對1000000007取模的結果輸出。 即輸出P%1000000007
輸入描述:
題目保證輸入的數組中沒有的相同的數字

數據範圍:

    對於%50的數據,size<=10^4

    對於
%75的數據,size<=10^5 對於%100的數據,size<=2*10^5
例如:
輸入:
1,2,3,4,5,6,7,0
輸出:7

我們可以發現,其實就是要找 每個數的左邊的比他大的數一共有多少個?

(1)第一想法:兩個for循環可以解決問題,時間復雜度 O(N^2),空間復雜度 O(1)。那麽能做到時間更短嗎?

(2)第二種做法:既然要找左邊的比當前數大的一共有多少個,那麽歸並排序過程中正好是比較左右兩邊的大小,那我們在這個過程中順便統計下左邊比右邊大的數量。

正如上圖所示:

(1)a,b兩步是對數組進行分解

(2)c,d兩步將數組進行合並,我們可以再這個過程中統計 左邊比右邊數大的個數。

我們在統計過程中,將已經合並的按照從小到大的過程進行排序,在合並的過程中,我們只用統計左邊一段,對於右邊一段中每個數產生的逆序對即可,因為不論左邊這段,還是右邊這段,在生成的過程中都是 由 更小的兩個小段 合並而成的,在這個過程中已經統計過了產生的逆序數。

我們圖解一次,統計逆序數量的合並過程。

技術分享圖片

(1)左右兩個子數組的最後邊出發,取出 7, 6,發現7>6說明,7可以對 4,6產生逆數對,即:左邊這個數 可以對 右邊 沒有遍歷到的其他數均會產生 逆數對,因為 他比這些數都要大,統計 逆數對數量,7放入copy

(2)然後左邊指針 往前走一步,取出:5,6,對於6來說沒有產生逆數對且不能確定6前面的數是否會產生的逆數對,所以將6復制到copy中,

(3)右邊指針往前走一步,取出:5,4,則5對於 右邊沒有遍歷過的數(也就是4和4前面的數)都會產生逆數對,進行統計,然後,將5放入copy

(4)此時 左邊數組元素全部遍歷完成,右邊的剩余元素 全部放入到copy中,此時copy按從小到大排序完成,逆數對統計完成。

class Solution {
public:
    int InversePairs(vector<int> data) {
        //0個或者1個
        if(data.size() == 0 || data.size() == 1) {
            return 0;
        }
        vector<int> copy = data;
        return sortMergeGetValue(data, copy, 0, data.size() - 1) % 1000000007;
    }
    
    long sortMergeGetValue(vector<int>& data, vector<int>& copy, long l, long r) {
        if(l >= r) {
            return 0;
        }
        //遞歸調用 歸並排序
        long mid = (l + r) /2;
        long leftCounts = sortMergeGetValue(copy, data, l, mid);
        long rightCounts = sortMergeGetValue(copy, data, mid + 1, r);
        
        long count = 0;
        long i = mid;  //左半端最後面
        long j = r;  //右半段最後面
        long index = r; //copy數組最右邊
        
        //當左右半段還沒有合並完成時,使用while循環
        while(i >= l && j > mid) {
            //判斷左半段 節點是不是 大於 右半段節點,說明左半段這個節點對於 右半段 對應節點 的前面所有節點 都會產生逆數對
            if(data[i] > data[j]) {
                count += (j - mid);
                copy[index--] = data[i--];
            } else {
                copy[index--] = data[j--];
            }
        }
        
        //如果左半段沒有合並完
        for(; i>= l; i--) {
            copy[index--] = data[i];
        }
        
        //右半段沒有合並完
        for(; j > mid; j--) {
            copy[index--] = data[j];
        }
        
        return count+ leftCounts + rightCounts;
    }
};

算法復雜度:

時間復雜度:O(N*Log(N))

空間復雜度:O(N)

歸並排序 之 逆序對