常見的五類排序算法圖解和實現(歸並類:二路歸並排序)


歸並類的排序算法

歸並:將兩個或兩個以上的有序表組合成一個新的有序表。

內部排序中,通常采用的是 2-路歸並排序。即:將兩個位置相鄰的記錄有序子序列歸並為一個記錄有序的序列。歸並排序是建立在歸並操作上的一種有效的排序算法。該算法是采用分治法(Divide and Conquer)的一個非常典型的應用。

圖解如下

看成是 n 個有序的子序列(長度為 1),然后兩兩歸並。

得到 n/2 個長度為2 或 1 的有序子序列。繼續亮亮歸並

最后一趟

代碼如下:

 1 //二路一次歸並過程的算法
 2 //low為本次二路歸並排序的第1有序區的第1個元素,i指向第1個元素, mid為第1有序區的最后1個元素
 3 void merge(int List[], int low, int mid, int high)
 4 {
 5     //mid+1為第2有序區第1個元素,mid為第1有序區的最后1個元素
 6     //i 指向第 1 有序區的第 1 個元素
 7     int i = low;
 8     //j 指向第 2 有序區的第 1 個元素,high 為第 2 有序區的最后一個元素
 9     int j = mid + 1;
10     //temp數組暫存合並的有序序列
11     int *temp = new int[high - low + 1];
12     //設置臨時數組的指示標志 k
13     int k = 0;
14     //內存分配失敗
15     if(!temp){
16         cout<<"數組分配失敗!";
17         exit(0);
18     }
19     //順序選取兩個有序區的較小元素,存儲到t數組中,因為是遞增排序
20     while(i <= mid && j <= high){
21         //較小的元素,存入temp臨時數組中
22         if(List[i] <= List[j]){
23             temp[k++] = List[i++];
24         }else{
25             temp[k++] = List[j++];
26         }
27     }// end of while
28     //比完之后,假如第1個有序區仍有剩余,則直接全部復制到 temp 數組
29     while(i <= mid){
30         temp[k++] = List[i++];
31     }
32     //比完之后,假如第2個有序區還有剩余,則直接全部復制到 temp 數組
33     while(j <= high){
34         temp[k++] = List[j++];
35     }
36     //將排好序的序列,重存回到 list 中 low 到 high 區間
37     for(i = low, k = 0; i <= high; i++, k++){
38         List[i] = temp[k];
39     }
40     //delete [] 刪除動態數組的內存
41     delete []temp;
42 }
43 
44 //遞歸實現二路歸並排序(也就是分治法的思想)
45 void mergeSort(int List[], int low, int high)
46 {
47     //二路歸並排序,分為二路
48     int mid = (low + high) / 2;
49     //終止條件,low >= high, 不是while,且不含等號,否則死循環
50     if(low < high)
51     {
52         //遞歸過程,二路歸並排序遞歸過程
53         mergeSort(List, low, mid);
54         mergeSort(List, mid + 1, high);
55         //歸並
56         merge(List, low, mid, high);
57     }
58 }
59 
60 int main(void)
61 {
62     int source[7] = {49, 38, 65, 97, 76, 13, 27};
63     
64     mergeSort(source, 0, 6);
65     
66     for (int i = 0; i < 7; i++) {
67         printf(" %d  ", source[i]);
68     }
69     
70     return 0;
71 }

上述代碼使用的是遞歸的方式,遞歸函數里,遞歸語句之前的語句和各級被調的遞歸函數執行順序一致,而遞歸語句之后的語句和被調的遞歸函數執行順序相反。這就是為什么 merge 函數要放在遞歸語句(兩條遞歸語句)之后,因為是逆向執行的。聯系二路歸並排序的過程!!

還可以使用非遞歸的方式,代碼如下:

 1 //非遞歸算法實現二路歸並排序,length代表數組長度,即數組最大下標是 legth - 1
 2 void mergeSort(int List[],int length)
 3 {
 4     //回憶圖解的過程,二路歸並算法的流程,不同於遞歸,遞歸是先遞歸語句,然后歸並函數,這樣歸並函數是倒序執行(和遞歸函數執行順序相反)
 5     int size = 1;
 6     int low;
 7     int mid;
 8     int high;
 9     //size 是標記當前各個歸並序列的high-low,從1,2,4,8,……,2*size
10     while(size <= length - 1)
11     {
12         //從第一個元素開始掃描,low代表第一個分割的序列的第一個元素
13         low = 0;
14         //當前的歸並算法結束的條件
15         while(low + size <= length - 1)
16         {
17             //mid代表第一個分割的序列的最后一個元素
18             mid = low + size - 1;
19             //high 代表第二個分割的序列的最后一個元素
20             high = mid + size;
21             //判斷一下:如果第二個序列個數不足size個
22             if(high > length - 1){
23                 //調整 high 為最后一個元素的下標即可
24                 high = length - 1;
25             }
26             //調用歸並函數,進行分割的序列的分段排序
27             merge(List, low, mid, high);
28             //打印出每次歸並的區間
29             cout << "low:" << low << " mid:" << mid << " high:" << high << endl;
30             //下一次歸並時第一序列的第一個元素位置
31             low = high + 1;
32         }// end of while
33         //范圍擴大一倍,二路歸並的過程
34         size *= 2;
35     }// end of while
36 }

二路歸並排序算法分析

每趟歸並的時間復雜度為O(n),共需進行 log2 n 趟。

二路歸並排序的時間復雜度:等於歸並趟數與每一趟時間復雜度的乘積。時間復雜度為O(nlog2n)。

利用二路歸並排序時,需要利用與待排序數組相同的輔助數組作臨時單元,故該排序方法的空間復雜度為O(n)比前面介紹的其它排序方法占用的空間大。

由於二路歸並排序中,每兩個有序表合並成一個有序表時,若分別在兩個有序表中出現有相同排序碼,則會使前一個有序表中相同排序碼先復制,后一有序表中相同排序碼后復制,從而保持它們的相對次序不會改變。所以,二路歸並排序是一種穩定的排序方法。

歸並的思想主要用於外部排序:

外部排序可分兩步
①待排序記錄分批讀入內存,用某種方法在內存排序,組成有序的子文件,再按某種策略存入外存。
②子文件多路歸並,成為較長有序子文件,再記入外存,如此反復,直到整個待排序文件有序。
外部排序可使用外存、磁帶、磁盤,最初形成有序子文件長取決於內存所能提供排序區大小和最初排序策略,歸並路數取決於所能提供排序的外部設備數。

 

歡迎關注

dashuai的博客是終身學習踐行者,大廠程序員,且專注於工作經驗、學習筆記的分享和日常吐槽,包括但不限於互聯網行業,附帶分享一些PDF電子書,資料,幫忙內推,歡迎拍磚!

 


免責聲明!

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



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