合并排序的非递归实现(自底向上设计)
上一篇博文,討論了合并排序的遞歸實現。這篇文章,說說合并排序的非遞歸實現。
思路描述
假設一個數組,共有11個(0到10)元素。
首先,進行“1+1”合并:即第0個和第1個合并,第2個和第3個合并,……,第8個和第9個合并,第10個不用合并;
然后,進行“2+2”合并:0~3合并,4~7合并,此時剩下3個,雖然不足4,但是大于2,所以做“2+1”合并,即8~10合并;
再然后,進行“4+4”合并,即0~7合并,此時剩下3個,3小于等于4,所以不用合并;
最后,進行“8+8”合并,因為總數是11,雖然不足16,但是大于8,所以做“8+3”合并.
也許上面的描述把你搞暈了,沒關系,看看示意圖,秒懂。
下圖共11個元素,實線箭頭表示合并,虛線箭頭表示不做處理。
C語言代碼
#include <stdio.h> #include <string.h> //memcpy函數用這個頭文件 #include <stdlib.h> //malloc和free函數用這個頭文件/**此函數為了測試用,功能是打印數組,不想刷屏,所以沒有加換行*a是數組首地址*len是數組長度*/ void print_array(int *a, int len) {int i=0;for(i=0; i<len; ++i)printf("%d ",a[i]); }/** 將兩個有序數組b和c合并為一個有序數組a* b是第一個非降序數組的首地址,長度是len_b* c是第二個非降序數組的首地址,長度是len_c* a指向輸出數組的首地址* 注意:a數組需要調用者分配空間,且a數組不能與b或者c重疊*/void __merge(int *a, int *b, int len_b, int *c, int len_c) {int i = 0; // b的下標int j = 0; // c的下標int k = 0; // a的下標int len = len_b + len_c; //計算出a數組的長度/*循環執行條件是 i 和 j 都沒有越界*/while(i < len_b && j < len_c) //&&的優先級更低 {/*比較b[i]和c[j],把較小的復制到a[k],同時i(或j)和k指向下一個元素*/if(b[i] <= c[j])a[k++] = b[i++];elsea[k++] = c[j++];}if(i == len_b) //說明b已經處理完memcpy(a+k, c+j, (len-k)*sizeof(int));else //說明c已經處理完memcpy(a+k, b+i, (len-k)*sizeof(int)); }void __merge_two_part(int a[], int len_1, int len_2) {int *sub_1 = a;int *sub_2 = a + len_1;/* 這里可以添加調試信息,展示排序過程,比如printf("merge ");print_array(sub_1,len_1);printf(" and ");print_array(sub_2,len_2);*/int temp_len = sizeof(int) * (len_1 + len_2); //計算需要多少字節int *temp = malloc(temp_len); //分配臨時空間if(temp == NULL){printf("malloc failed\n");return;}__merge(temp,sub_1,len_1,sub_2,len_2);memcpy(a,temp,temp_len); //此時數組a已經有序/* 這里可以添加調試信息,展示排序過程,比如printf("---> ");print_array(a,len_1 + len_2);printf("\n");*/free(temp); }void __n_and_n_merge(int a[], int a_len, int n) {int left = a_len; //left用來計數,表示還剩多少個元素沒有參與合并int join_len = n + n; // n并n,合并后的長度是2n,用join_len表示while(left >= join_len){__merge_two_part(a,n,n);a += join_len; //使a指向下一段left -= join_len;}/*當不夠n+n合并時,如果超過n個元素,仍然進行合并;如果剩余元素小于等于n個,則不做處理*/ if(left > n) {__merge_two_part(a,n,left-n);} }void merge_sort_down2up(int a[], int len) {int step = 1; //初始步長while(step < len){__n_and_n_merge(a,len,step); //step + step 合并step *= 2; } }//測試函數 int main(void) {int a[11]={9,8,5,3,2,9,4,6,7,1,0}; //11個數 merge_sort_down2up(a,11);printf("最終結果:");print_array(a,11);printf("\n");return 0; }代碼講解
已經在代碼中添加了詳細的注釋,這里只說要點。
與排序相關的函數有4個。
前三個屬于內部函數(我以雙下劃線開頭),第四個merge_sort_down2up屬于開放給用戶的函數,調用的時候傳入整形數組名和長度即可。
void __merge(int *a, int *b, int len_b, int *c, int len_c);
這個函數在上一篇博文中出現過,屬于歸并過程中最基本的“磚頭”,此函數被第二個函數__merge_two_part調用。
void __merge_two_part(int a[], int len_1, int len_2);
為了增強整個代碼的可讀性,我專門設計了這個接口。
這個函數的目的是把一個數組的前半部分(已經為升序)和后半部分(已經為升序)進行合并排序。int a[]也可以寫為int *a,用來接收數組首地址;len_1表示前半部分的長度,len_2表示后半部分的長度。
假設數組a包含7個元素,前4個和后3個元素已經分別有序,現在要做4+3合并,那么應該這樣用
__merge_two_part(a, 4, 3);
示意圖如下:
調用后,數組a為升序。
void __n_and_n_merge(int a[], int a_len, int n);
此函數對整個數組進行n+n合并。
注意:對于剩余的元素,當不夠n+n合并時,如果大于n個元素,則進行n+x合并; 如果小于等于n個,則不做處理.
a_len表示數組長度,每次調用時保持不變,n表示合并的步長,取值為1,2,4,8...;
n的初始值為1,反復調用此函數,調用后n取值翻倍,直到n大于等于a_len為止。
在void __merge_two_part(int a[], int len_1, int len_2)函數中添加一些打印信息,可以看出整個排序過程。截圖如下:
【參考資料】
http://www.cnblogs.com/xing901022/p/3671771.html
總結
以上是生活随笔為你收集整理的合并排序的非递归实现(自底向上设计)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 用冒泡法对10个整数从小到大排序
- 下一篇: 程序员面试系列——有符号数的溢出