歸並排序


【1】歸並排序

歸並排序是建立在歸並操作上的一種有效的排序算法。該算法也是采用分治法(Divide and Conquer)的一個非常典型的應用。

歸並排序的算法復雜度為O(N*logN)。

歸並排序算法是穩定的(參見隨筆《常用排序算法穩定性分析》)。

【2】歸並排序邏輯分析與代碼實現

在分析歸並排序的邏輯之前,讓我們也利用一下分治法理念:先從基層做起(個人之拙見)。

先考慮一個簡單問題:如何將兩個有序數列進行合並?(注意:已有序數列)

好吧!其實,這個簡單的問題會給我們很大的啟迪。步驟整理如下:

<1>只要把兩個待合並數列的第一個數據進行比較,哪個小就先安置哪個,排序之后就在對應數列中跳過該數據索引(下標)。

<2>重復以上過程,直至有一個數列已經完全安置(即已為空)。

<3>再將另一個數列(未空數列)的所剩數據直接取出即可。

(1)示例代碼如下:

 1 #include<iostream>
 2 using namespace std;  3 
 4 //將有序數組ar[]和br[]合並到cr[]中
 5 void MemeryArray(int a[], int n, int b[], int m, int c[])  6 {  7     int i, j, k;  8 
 9     i = j = k = 0; 10     while (i < n && j < m) 11  { 12         if (a[i] < b[j]) 13             c[k++] = a[i++]; 14         else
15             c[k++] = b[j++]; 16  } 17 
18     while (i < n) 19         c[k++] = a[i++]; 20 
21     while (j < m) 22         c[k++] = b[j++]; 23 } 24 
25 void  PrintArr(int ar[],int n) 26 { 27     for(int i = 0; i < n; ++i) 28         cout<<ar[i]<<" "; 29     cout<<endl; 30 } 31 
32 void main() 33 { 34     int ar[5] = {12, 23, 34, 45, 56}; 35     int br[5] = {13, 24, 35, 46, 60}; 36     int cr[10]; 37     cout<<"數組ar為:"<<endl; 38     PrintArr(ar, 5); 39     cout<<"數組br為:"<<endl; 40     PrintArr(ar, 5); 41     MemeryArray(ar, 5, br, 5, cr); 42     cout<<"合並后結果為:"<<endl; 43     PrintArr(cr, 10); 44 } 45 
46 /*
47 數組ar為: 48 12 23 34 45 56 49 數組br為: 50 12 23 34 45 56 51 合並后結果為: 52 12 13 23 24 34 35 45 46 56 60 53 */

可以看出合並有序數列的效率是比較高的,完全可以達到O(n)。

那么,解決了上面的合並有序數列問題之后,我們再來看歸並排序。

歸並排序的基本思路就是先將待排序數組分成兩組A,B,然后如果這兩組組內的數據都是有序的,就可以利用上面的邏輯(合並有序數列邏輯)很方便的將這兩個有序數組數據再進行合並排序。

問題關鍵是如何讓這兩組組內數據有序呢?

可以將A,B組各自再分成二組。依次類推,當分出來的小組只有一個數據時,可以認為這個小組組內已經達到了有序,然后再合並相鄰的兩個小組就可以了。

這樣通過先遞歸的分解待排序數列,再合並數列就完成了歸並排序的過程。實現歸並排序。

仔細想,仔細想,仔細想,先想明白這幾句話,再看下面的代碼。

(2)歸並排序實現及測試示例代碼:

 1 #include<iostream>
 2 using namespace std;  3 
 4 #define  MAXSIZE  10
 5 
 6 //將兩個有序數列a[first...mid] 和 a[mid...last] 合並。
 7 void mergearray(int a[], int first, int mid, int last, int temp[])  8 {  9     int i = first, j = mid + 1; 10     int m = mid, n = last; 11     int k = 0; 12 
13     while (i <= m && j <= n) 14  { 15         if (a[i] <= a[j]) 16             temp[k++] = a[i++]; 17         else
18             temp[k++] = a[j++]; 19  } 20 
21     while (i <= m) 22         temp[k++] = a[i++]; 23 
24     while (j <= n) 25         temp[k++] = a[j++]; 26 
27     for (i = 0; i < k; ++i) 28         a[first + i] = temp[i]; 29 } 30 void mergesort(int a[], int first, int last, int temp[]) 31 { 32     if (first < last) 33  { 34         int mid = (first + last) / 2; 35         mergesort(a, first, mid, temp);     //左邊有序
36         mergesort(a, mid + 1, last, temp);  //右邊有序
37         mergearray(a, first, mid, last, temp); //再將兩個有序數列合並
38  } 39 } 40 
41 bool MergeSort(int a[], int n) 42 { 43     int *p = new int[n]; 44     if (p == NULL) 45         return false; 46     mergesort(a, 0, n - 1, p); 47  delete[] p; 48     return true; 49 } 50 
51 void  PrintArr(int ar[],int n) 52 { 53     for(int i = 0; i < n; ++i) 54         cout<<ar[i]<<" "; 55     cout<<endl; 56 } 57 
58 void main() 59 { 60     int ar[MAXSIZE] = {23, 34, 45, 78, 90, 12, 49, 92, 32, 19}; 61  PrintArr(ar, MAXSIZE); 62     bool bValue = MergeSort(ar, MAXSIZE); 63     if(!bValue) 64  { 65         cout<<"MergeSort Failed!! "<<endl; 66  } 67  PrintArr(ar, MAXSIZE); 68 }

以上內容參考文章:白話經典算法系列之五 歸並排序的實現

(3)另外一種代碼實現:

 1 #include<iostream>
 2 #include<malloc.h>
 3 using namespace std;  4 
 5 #define   MAXSIZE  10
 6 
 7 void  PrintArr(int ar[],int n)  8 {  9     for(int i = 0; i < n; ++i) 10         cout<<ar[i]<<" "; 11     cout<<endl; 12 } 13 
14 static void merge(int ar[], int low, int mid, int high) 15 { 16     int i, k = 0; 17     //申請空間,使其大小為兩個已經排序序列之和,該空間用來存放合並后的序列 
18     int *temp = (int *)malloc((high - low + 1)*sizeof(int)); 19     int begin1 = low; 20     int end1 = mid; 21 
22     int begin2 = mid + 1; 23     int end2 = high; 24 
25     //比較兩個元素,選擇相對小的元素放入到合並空間,並移動指針到下一位置 
26     for (k = 0; begin1 <= end1 && begin2 <= end2;) 27  { 28         if(ar[begin1] < ar[begin2]) 29             temp[k++] = ar[begin1++]; 30         else  
31             temp[k++] = ar[begin2++]; 32  } 33 
34     while(begin1 <= end1)  //若第一個序列有剩余,直接拷貝出來粘到合並序列尾 
35         temp[k++] = ar[begin1++]; 36     while(begin2 <= end2)  //若第二個序列有剩余,直接拷貝出來粘到合並序列尾 
37         temp[k++] = ar[begin2++]; 38 
39     for (i = 0;i < k; i++)   //將排序好的序列拷貝回數組中 
40  { 41         ar[low+i] = temp[i]; 42  } 43            
44  free(temp); 45 } 46 void merge_sort(int ar[],int begin,int end) 47 { 48     int mid = 0; 49     if(begin < end) 50  { 51         mid = (begin + end) / 2; 52  merge_sort(ar, begin, mid); 53         merge_sort(ar, mid + 1, end); 54  merge(ar, begin, mid, end); 55  } 56 } 57 
58 void main() 59 { 60     int  ar[] = {12, 14, 54, 5, 6, 3, 9, 8, 47, 89}; 61     merge_sort(ar, 0, MAXSIZE-1); 62  PrintArr(ar, MAXSIZE); 63 } 64 
65 /*
66 *3 5 6 8 9 12 14 47 54 89 67  */

推薦掌握第一種。第二種僅僅助於理解歸並排序思想。當然,實現方式很多,那種好理解就使用那種,因人而異。

Good Good Study, Day Day Up.

順序  選擇  循環  堅持  總結 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM