歸併排序(遞迴實現和迭代實現)
阿新 • • 發佈:2018-12-23
//首先是遞迴實現的方式
#include<stdio.h> #define MAXSIZE 10 //實現歸併,並把資料都放在list1裡面 void merging(int *list1,int list1_size,int *list2,int list2_size) { int i,j,k,m; i = j = k = 0; int temp[MAXSIZE]; while(i<list1_size&&j<list2_size) { if(list1[i] < list2[j]) { temp[k++] = list1[i++]; } else { temp[k++] = list2[j++]; } } //如果有剩下的,那麼說明就是它是比前面的陣列都大的,直接加入就可以了 while(i<list1_size) { temp[k++] = list1[i++]; } while(j<list2_size) { temp[k++] = list2[j++]; } for(m=0; m<(list1_size + list2_size);m++) { list1[m] = temp[m]; } } void MergeSort(int k[],int n) { if(n>1) { int *list1 = k; //定義一個指標變數,指向陣列k的地址 int list1_size = n/2; //陣列的長度分為本來陣列長度的一半 int *list2 = k +n/2; //定義另外一個指標變數,指向陣列k+n/2的地址 int list2_size = n - list1_size;//長度為剛才總的減去剛才分去那一半 MergeSort(list1,list1_size); //呼叫陣列本身,相當與遞迴, MergeSort(list2,list2_size); //呼叫陣列本身,相當與遞迴 merging(list1,list1_size,list2,list2_size); } } int main(){ int i,a[10] = {5,2,6,0,3,9,1,7,4.8}; MergeSort(a,10); printf("排序後的結果是:"); for(i=1; i<10; i++) { printf("%d",a[i]); } printf("\n\n"); return 0; } //歸併排序複雜度分析:一趟歸併需要將待排序列中的所有記錄 //掃描一遍,因此耗費時間為O(n),而由完全二叉樹的深度可知, //整個歸併排序需要驚醒[log2n],因此,總的時間複雜度為 //O(nlogn),而且這是歸併排序演算法中最好、最壞平均的時間效能 //空間複雜度:由於歸併過程中需要與原始記錄序列同樣數量級的 //儲存空間去存放歸併結果及遞迴深度為log2N的棧空間,因此空間 //複雜度為O(n+logN) //也就是說,歸併排序是一種比較佔記憶體,但卻效率高且穩定的演算法
沒有最好只有更好,下面介紹一種用迭代方式實現歸併排序的演算法:
非遞迴的方法,避免了遞迴時深度為log2N的棧空間,空間只是用到歸併臨時申請的跟原來陣列一樣大小的空間,並且在時間效能上也有一定的提升,因此,使用歸併排序是,儘量考慮用非遞迴的方法。
#include<stdio.h> #include<stdlib.h> #define MAXSIZE 10 void MergeSort(int k[],int n) { int i,next,left_min,left_max,right_min,right_max; //開闢一個與原來陣列一樣大小的空間用來儲存用 int *temp = (int *)malloc(n * sizeof(int)); //逐級上升,第一次比較2個,第二次比較4個,第三次比較8個。。。 for(i=1; i<n; i*=2) { //每次都從0開始,陣列的頭元素開始 for(left_min=0; left_min<n-i; left_min = right_max) { right_min = left_max = left_min + i; right_max = left_max + i; //右邊的下標最大值只能為n if(right_max>n) { right_max = n; } //next是用來標誌temp陣列下標的,由於每次資料都有返回到K, //故每次開始得重新置零 next = 0; //如果左邊的資料還沒達到分割線且右邊的陣列沒到達分割線,開始迴圈 while(left_min<left_max&&right_min<right_max) { if(k[left_min] < k[right_min]) { temp[next++] = k[left_min++]; } else { temp[next++] = k[right_min++]; } } //上面迴圈結束的條件有兩個,如果是左邊的遊標尚未到達,那麼需要把 //陣列接回去,可能會有疑問,那如果右邊的沒到達呢,其實模擬一下就可以 //知道,如果右邊沒到達,那麼說明右邊的資料比較大,這時也就不用移動位置了 while(left_min < left_max) { //如果left_min小於left_max,說明現在左邊的資料比較大 //直接把它們接到陣列的min之前就行 k[--right_min] = k[--left_max]; } while(next>0) { //把排好序的那部分陣列返回該k k[--right_min] = temp[--next]; } } } } int main(){ int i,a[10] = {5,2,6,0,3,9,1,7,4,8}; MergeSort(a,10); printf("排序後的結果是:"); for(i=0; i<10; i++) { printf("%d",a[i]); } printf("\n\n"); return 0; }