排序演算法:歸併排序(Merge Sort)
阿新 • • 發佈:2020-10-07
歸併排序
歸併排序採用了分治策略(divide-and-conquer),就是將原問題分解為一些規模較小的相似子問題,然後遞迴解決這些子問題,最後合併其結果作為原問題的解。
歸併排序將排序陣列A[1..n]分成兩個各含n/2個元素的子序列,然後對這個兩個子序列進行遞迴排序,最後將這兩個已排序的子序列進行合併,即得到最終排好序的序列:
**歸併排序的時間複雜度為:O(nlgn),其中 MERGE(a,b,c,d)的時間複雜度為O(n)。**如果這二組組內的資料都是有序的,那麼就可以很方便的將這二組資料進行排序。
- 將n個元素分成各含有n/2個元素的子序列.
- 對兩個子序列遞迴排序.
- 合併兩個已排序的子序列以得到排序結果.
1 void merge(int x,int y){ 2 if(x==y)return; 3 int mid=(x+y)/2,i=x,j=mid+1,k=x; 4 merge(x,mid),merge(mid+1,y); 5 while(i<=mid&&j<=y){ 6 if(a[i]<=a[j]){ 7 c[k++]=a[i++]; 8 }else{ 9 c[k++]=a[j++],ans+=mid-i+1; 10 }11 } 12 while(i<=mid)c[k++]=a[i++]; 13 while(j<=y)c[k++]=a[j++]; 14 for(int l=x;l<=y;l++){ 15 a[l]=c[l]; 16 } 17 }
歸併排序的缺點:不是原地排序,需要額外申請空間來進行排序。
逆序數對求數
給定包含n個不同元素的陣列A[1..n],如果i < j且A[i] > A[j],則(i, j)稱為陣列A的一個逆序。陣列包含所有逆序的數量成為陣列的逆序數。例對於陣列[1, 2, 3, 4, 5],它的逆序有(2, 1), (3, 1), (5, 4), (5, 1), (4, 1),所以該陣列的逆序數為5。
這裡如果用氣泡排序絕對會RE,所以歸併排序。可以看出,消除逆序的操作發生在while迴圈中:每執行一次迴圈體都會消除一個逆序。當所有逆序都被消除後,就完成了插入排序。所以,可以通過在插入排序中加入計數計算陣列逆序數。
原陣列的逆序數是兩個子陣列逆序數之和加上合併過程中消除的逆序數。
1 while(i <= mid && j <= r){ 2 if(a[i] > a[j]){ 3 ans += mid - i + 1; 4 b[t++] = a[j]; 5 ++j; 6 }else{ 7 b[t++] = a[i]; 8 ++i; 9 } 10 } 11
建議輸入速度更快比如快讀。