歸併排序以及逆序對問題
阿新 • • 發佈:2020-12-17
歸併排序以及逆序對問題
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加上本層遞迴所算出來的逆序對個數即可。
可以很明顯的看出,要先放置1在temp中,放了1之後,從i~mid都會比1大,因為程式中經過了此次對比
if(q[i] > q[j]) tmp[k++] = q[j++];
在左右兩邊均為有序的狀態下,左邊會有i - mid+1個數比此時的q[j]大,所以要用 res+=i-mid+1