標籤:歸併排序 遞迴 合并 merge 二路歸併
歸併排序(Merge Sort)是將兩個(或兩個以上)有序表合并成一個新的有序表,即把待排序序列分為若干個有序的子序列,再把有序的子序列合并為整體有序序列。
歸併排序的具體做法:
- 把原序列不斷地遞迴等分,直至每等份只有一個元素,此時每等份都是有序的。
- 相鄰等份合并,不斷合并,直至合并完全。
二路歸併
歸併排序是建立在歸併操作上的一種有效排序演算法。該演算法是採用分治法(Divide and Conquer)的一個非常典型的應用。歸併排序最常用的是二路歸併,即把兩個小的有序的序列和並成一個大的有序序列:合二為一。
一個二路歸併的流程圖是這樣的:
多路歸併無非是多個有序的小序列合并成一個大的有序序列,道理和二路歸併一樣。
先來看下如何把兩個有序的序列合并成一個大的有序序列,代碼如下:
/* *把有序序列a和b,合并成c *該演算法成立前提: a和b已經有序 */ void merge(int a[], int na, int b[], int nb, int c[]){if(a && b && c && na >0 && nb >0){int i,j,k;i = j = k = 0;//不斷地比較a和b的頭部元素,較小的存入c while(i < na && j < nb){if(a[i] <= b[j]) // <= 保持演算法的穩定性c[k++] = a[i++];elsec[k++] = b[j++];/*另一種更有效做法是這樣的 while(i < na && a[i] <= b[j])c[k++] = a[i++];while(j < nb && b[j] < a[i])c[k++] = b[j++];*/}//把a或b中剩餘的元素直接存入c /* 也可以這樣: * memcpy(c+k, a+i, (na-i)sizeof(int)); * 下同 */while(i < na)c[k++] = a[i++];while(j < nb)c[k++] = b[j++];}}
可以看出,二路歸併的時間複雜度是O(n),n是原序列的資料規模。以上代碼是歸併排序的基礎,弄懂了它,就很好寫歸併排序了,看下歸併排序的流程圖:
可以看出,上半部分不斷地遞迴深入:不斷地均分原序列,直到每一部分只含有一個元素。下半部分,開始遞迴返回,通過反覆調用二路歸併演算法,把相鄰的有序子序列合并成一個規模更大的序列。
理解了這些,相信就很容易寫出歸併排序的代碼了:
//把[first, mid]和[mid+1, last]範圍內的資料合併 void mergeArray(int a[], int b[], int first, int mid, int last){int i, j, k;i = first, j = mid + 1, k = 0;while (i <= mid && j <= last){while(i <= mid && a[i] <= a[j])b[k++] = a[i++];while(j <= last && a[j] < a[i])b[k++] = a[j++];}/* 也可以這樣: * memcpy(b+k, a+i, (mid-i+1)sizeof(int)); * 下同 */while (i <= mid)b[k++] = a[i++];while (j <= last)b[k++] = a[j++];//[first,last]範圍內的資料已有序,則寫回原數組for (i = 0; i < k; i++)a[first + i] = b[i];}void mergesort(int a[], int b[], int first, int last){if (first < last){int mid = first + ((last - first) >> 1);mergesort(a, b, first, mid);mergesort(a, b, mid + 1, last);mergeArray(a, b, first, mid, last);}}void MergeSort(int a[], int n){if (a && n > 1){int *b = new int[n]; //構建輔助數組mergesort(a, b, 0, n - 1);delete[]b;}}
在排序過程中,我們使用了一個相同大小的臨時輔助數組。
演算法分析:
1.演算法的複雜度
對數組長度為n的序列進行歸併排序,則大約要進行logn次歸併,每一次合并都是線性時間O(n)。故粗略的計算出歸併排序的時間複雜度是O(nlogn)(最好、最差都是這樣)。空間複雜度是O(n)。詳細的時間複雜度分析是這樣的:
對長度為n的序列歸併排序,需要遞迴的對長度為n/2的子序列進行歸併排序,最後把兩段子序列二路歸併。遞推關係是這樣的:T(n)=2T(n/2)+O(n),顯然T(1)=O(1),解得T(n)=o(nlogn)。
2.穩定性
歸併排序是穩定的,並且是時間複雜度為o(nlogn)的幾種排序(快速排序、堆排序)中唯一穩定的排序演算法。
3.儲存結構
順序儲存和鏈式儲存都行。
另外,歸併排序多用於外排序中。
轉載請註明出處,本文地址:http://blog.csdn.net/zhangxiangdavaid/article/details/34463409
若是有所協助,頂一個哦!
專欄目錄:資料結構與演算法目錄