歸併排序(包含逆序數對的個數51Nod1019)
阿新 • • 發佈:2018-11-09
歸併排序是效率很好的排序方式,和快排效率一樣高,但在穩定性上優於快排,下面我們來介紹歸併排序。
歸併排序運用遞迴將序列不斷二分(其原理就是分治),就像一棵樹不斷向下分支,最後分到只剩一個元素,這樣這個元素就可當做有序的,因為只有一個元素嘛。然後是合併,怎麼分出來就怎麼合併回去,不過既然是排序,那麼合併的時候就需要比較一下大小了。
下面為了更好的理解,我們來看一張圖片。(這張圖是借用的,很感謝製圖人)
這就是一個簡單序列的歸併排序過程。
下面讓我們來看程式碼。
#include<cstdio> const int Max=50001; int temp[Max],num=0; void mergearray(int a[],int first,int mid,int last){//將陣列按順序合併 int i=first,j=mid+1,m=mid,n=last,k=0; while(i<=m&&j<=n){//這裡的等號很重要,沒有的話前半段的資料不能全部遍歷 if(a[i]<=a[j]) temp[k++]=a[i++]; else{ temp[k++]=a[j++]; num+=mid-i+1; //統計逆序數對 } } while(i<=m) temp[k++]=a[i++]; //這裡等號很重要,不然會漏資料 while(j<=n) temp[k++]=a[j++];//這裡等號很重要,不然會漏資料 for(int q=0;q<=last-first;q++) a[first+q]=temp[q]; } void mergesort(int a[],int first,int last){//將陣列二分處理 if(first<last){ int mid=(first+last)/2; mergesort(a,first,mid); //左邊有序 mergesort(a,mid+1,last);//右邊有序 mergearray(a,first,mid,last); } } int main() { int a[Max],n; scanf("%d",&n); for(int i=0;i<n;i++){ scanf("%d",&a[i]); } mergesort(a,0,n-1); for(int i=0;i<n;i++) printf("%d\n",a[i]);//統計逆序數的話輸出num即可。 }
這裡可能有的人不太明白逆序數為什麼num+=mid-i+1這樣算,這裡做一下說明,在數組合並時計算前面的數是否比後面的大,這裡要注意合併時前後兩部分已經是有序,如果此時啊a[i]>a[j],說明a[first]到a[i-1]全部小於a[j],而a[mid+1]到a[j-1]全部小於a[j],那麼意思就是大於a[j]的數全部在a[i]到a[mid]之間,a[i]到a[mid]共有mid-i+1個數,所以逆序數對num此時要加上mid-i+1。
本人實力有限,如有錯誤,歡迎指出,謝謝。