最近一段時間一直在做項目,沒有時間(好吧,我也承認最近有點懶,晚上回去什么都不想干了)。不過最近這兩天晚上還是看了一點,就寫下來備忘吧。
排序算法之歸並排序:看資料都介紹說這是一種效率非常高的算法,看有的大神進行的測試,在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 }
該博客系博主自主原創,轉載請標明出處喲。