數據結構排序算法之歸並排序


  最近一段時間一直在做項目,沒有時間(好吧,我也承認最近有點懶,晚上回去什么都不想干了)。不過最近這兩天晚上還是看了一點,就寫下來備忘吧。

  排序算法之歸並排序:看資料都介紹說這是一種效率非常高的算法,看有的大神進行的測試,在200000個隨機數的情況下排序速度比快排還要快。

  其實主要原理也非常簡單,看資料中的專業術語都是說采用分治法,分治法的要求是左右兩邊的數據都要有序(其實當我們左右兩邊只剩余一個的時候就是有序的)。

  其實所謂的分治法也就是把這堆數據划分開,左邊一堆,右邊一堆,然后在對左邊這堆進行歸並排序,也就是把左邊這堆在分治划分,也就是在分成左左一堆,左右一堆,然后在往下進行分治划分,直到每個小堆都只有一個數據的時候,划分就完成了,因為只剩下一個數據,所以我們默認這個小堆的數據是有序的(這其實也是廢話,就一個數,怎么看當然都是有序的了)。

  光分開肯定不行,因為我們要排序,所以我們還要在合起來,這就是我們的重點了,怎么合起來?這就需要進行數字大小的比較了,我們將兩個指針分別放在兩個左右小堆的開頭處(最左端),然后我們進行兩個指針下標對應數據的大小比較,若left[i]<=right[j],則我們將數字小的(即left[i])放在臨時數組temp[k]處,然后我們將i,k都進行加1操作,這樣的目的是將左邊的比較數據往下移動一位,將臨時數組的下標往下移動一位,以免被覆蓋,這樣我們將數據不斷進行比較,當有一個小序列已經到頭的時候,說明另外一個序列的剩余所有數據都比已經沒有的序列的數據大,我們將剩余的數字全部賦值給臨時數組。這樣就完成了合起來的操作。

  這面就是划分和合並的圖示。

  下面就是代碼,里面都有注釋。

 1 #include <stdio.h>
 2 
 3 void cutTwo(int sourceArr[],int *tempArr[],int start,int end);
 4 void merge(int sourceArr[],int *tempArr[],int start,int mid,int end);
 5 
 6 int main(int argc, char *argv[])
 7 {
 8     int a[8]={
 9         50, 10, 20, 30, 70, 40, 80, 60
10     };
11     int *b[8]={
12     };
13     int i;
14     cutTwo(a,b,0,8);
15     for(i=0;i<8;i++){
16         printf("%d ",a[i]);
17     }
18     return 0;
19 }
20 
21 
22 /*
23 歸並排序算法: 
24 */
25 void merge(int sourceArr[],int *tempArr[],int start,int mid,int end){
26     //當前我們有一個源數組,我們在比較時將這個源數組一分為二進行比較歸並排序
27     /*
28     因為我們要進行歸並排序,所以我們需要兩個指針分別指向兩個子序列的開始位置,即start指向左邊部分的開始位置,
29     mid+1指向右邊部分的開始位置,我們還需要一個k的下標,用於存儲我們交換過來的數組到臨時數組tempArr[] 
30     */ 
31     int i=start;  //定義一個i下標,用於表示左邊部分開始的位置下標
32     int j=mid+1;  //定義一個j下標,用於表示右邊部分開始的位置下標
33     int k=0;  
34     
35     /*
36     因為我們在比較時是不斷的比較,直到一個子序列到達最后,所以我們應該用while循環來做,結束條件:無非就是左邊序列到頭了
37     或者是右邊序列到頭了,即:i<=mid&&j<=end  只有在這兩個條件都成立的條件下說明兩個子序列都沒有到頭 
38     */
39     while(i<=mid&&j<=end){  //當i=mid+1或者j=end+1時說明子序列中有一個到頭了,則跳出循環 
40         if(sourceArr[i]<=sourceArr[j]){  //表示當前i比較小,那么我們就將小的值賦給k 
41             tempArr[k]=sourceArr[i];
42             k=k+1;
43             i=i+1;
44         }
45         else{
46             tempArr[k]=sourceArr[j];
47             k=k+1;
48             j=j+1;            
49         }
50         /*
51         不能將k,i,j的加1操作放在if else判斷的外面,因為我們在進行比較的時候,只是將下標所指的數字小的放在左邊,將下標所指的數字
52         大的不動,因為我們小的下標加1后還要和剛才下標所指的數字再次進行比較,如果放在外面,那么我們的比較的對象不對了(
53         因為的大的數字的下標加1了,前面的一個數字沒有進行比較) 
54         */
55     }
56     
57     /*
58     當有一個子序列到頭以后,我們就要將剩余沒到頭的子序列的剩余元素放到k的右邊,因為剩余的肯定是已經有序的,且肯定比已經
59     到頭的子序列的全部元素都要大的。 
60     */
61     while(j<=end){  //i==mid+1&&    因為左邊序列i跳出循環的條件肯定是當前i=mid+1了,則我們移動右邊j的子序列就好了 
62         tempArr[k]=sourceArr[j];
63         k++;
64         j++;
65     }
66     while(i<=mid){  // &&j==end+1   則此時表示當前跳出循環的是j右邊的子序列,則我們移動左邊的i的子序列 
67         tempArr[k]=sourceArr[i];
68         k++;
69         i++;
70     }
71     int m;
72     for(m=0;m<k;m++){
73         sourceArr[start+m]=tempArr[m];
74     }
75     
76     
77 } 
78 
79 
80 /*
81 上述操作完成僅僅是完成了對一個大的序列中一部分的排列,因為我們的做法是對整個的序列進行二分,二分之后對子序列進行歸並排序 
82 */
83 void cutTwo(int sourceArr[],int *tempArr[],int start,int end){    
84     
85     if(start<end){
86         int mid;  //設置一個取中間的變量
87         mid=(start+end)/2;
88         
89         cutTwo(sourceArr,tempArr,start,mid);
90         cutTwo(sourceArr,tempArr,mid+1,end);
91         merge(sourceArr,tempArr,start,mid,end);
92     }
93      
94 }

  該博客系博主自主原創,轉載請標明出處喲。


免責聲明!

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



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