1. 程式人生 > 實用技巧 >歸併排序以及逆序對問題

歸併排序以及逆序對問題

歸併排序以及逆序對問題

void merge_sort(int q[], int l, int r)
{
    if(l >= r) return;
    

    int mid = l+r>>1;
    merge_sort(q,l,mid);
    merge_sort(q,mid+1,r);
    
    int tmp[r-l+1]={0}, k = 0, i = l, j = mid+1;
    while(i<=mid && j<=r){
        if(q[i]<=q[j]) tmp[k++] = q[i++];
        else tmp[k++] = q[j++];
    }
    
    while(i<=mid) tmp[k++] = q[i++];
    while(j<=r) tmp[k++] = q[j++];
    
    for(int i = l, j = 0; i<=r; i++,j++) q[i] = tmp[j];
}

歸併排序與快速排序的分治合差別:

歸併是從下往上進行出遞迴的時候進行資料的處理,要進行合併。

快速排序是從上往下的時候進行分治,不用進行合併。

習題:

788.逆序對的數量

給定一個長度為n的整數數列,請你計算數列中的逆序對的數量。

逆序對的定義如下:對於數列的第 i 個和第 j 個元素,如果滿足 i < j 且 a[i] > a[j],則其為一個逆序對;否則不是。

輸入格式

第一行包含整數n,表示數列的長度。

第二行包含 n 個整數,表示整個數列。

輸出格式

輸出一個整數,表示逆序對的個數。

int res;
void merge_sort(int q[], int l, int r){
    if(l >= r) return;
    

    int mid = l + r >> 1;
    merge_sort(q, l ,mid);
    merge_sort(q, mid+1, r);
    
    int tmp[r-l+1] = {0}, k = 0 ,i = l, j = mid+1; 
    while(i <= mid&& j<=r){
        if(q[i] <= q[j]) tmp[k++] = q[i++];
        else{
            tmp[k++] = q[j++];
            //重點
            res+=mid-i+1;
        }
    }

    while(i <= mid) tmp[k++] = q[i++];
      
    
    while(j <= r) tmp[k++] = q[j++];
    
    for(int i = l, j = 0; i <= r; i++,j++) q[i] = tmp[j];

}

為什麼只在歸併排序的基礎上增加了:

res+=mid-i+1;

就能解出這道題?

假設在最後一次排列中的資料為

2 3 4 || 1 5 6

mid = 4 ; i =2; j = 5;

首先,兩部分資料在上一層遞迴中已經得到了排序,所以兩部分資料均為有序狀態。因此,上一層遞迴的逆序對已經算出。我們只需要將res加上本層遞迴所算出來的逆序對個數即可。

可以很明顯的看出,要先放置1temp中,放了1之後,從i~mid都會比1大,因為程式中經過了此次對比

if(q[i] > q[j]) tmp[k++] = q[j++];

在左右兩邊均為有序的狀態下,左邊會有i - mid+1個數比此時的q[j]大,所以要用 res+=i-mid+1

.