上一篇博文,讨论了合并排序的递归实现。这篇文章,说说合并排序的非递归实现。

思路描述

假设一个数组,共有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个。

void __merge(int *a, int *b, int len_b, int *c, int len_c);
void __merge_two_part(int a[], int len_1, int len_2);
void __n_and_n_merge(int a[], int a_len, int n);
void merge_sort_down2up(int a[], int len);

前三个属于内部函数(我以双下划线开头),第四个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

合并排序的非递归实现(自底向上设计)相关推荐

  1. 非递归快速排序php,快排序的非递归实现(原创)

    本文简要介绍快排序的非递归实现方式并给出了C语言版本的源代码,非递归实现由于没有函数调用的消耗,相对于递归方式有一定的优势. 在快排序的递归实现中,函数quicksort(int nlow, int ...

  2. 一步一步写算法(之非递归排序)

    [ 声明:版权所有,欢迎转载,请勿用于商业用途.  联系信箱:feixiaoxing @163.com] 在上面一篇博客当中,我们发现普通查找和排序查找的性能差别很大.作为一个100万的数据,如果使用 ...

  3. 【算法设计zxd】第2章 算法分析实例 递归非递归——交换,n阶乘,矩阵乘法,汉诺塔,二路归并,

    目录 非递归形式算法分析: 递归形式算法分析: [例2-9]交换a和b的值. 非递归 [例2-10]求n! 非递归: 思考题:韩信点兵 [例2-11]对于任意给定的n阶方阵A和B,求A×B的积C并且 ...

  4. 合并排序(C语言实现)

    递归算法是把一个问题分解成和自身相似的子问题,然后再调用自身把相应的子问题解决掉.这些算法用到了分治思想.其基本模式如下: 分解:把一个问题分解成与原问题相似的子问题 解决:递归的解各个子问题 合并: ...

  5. 归并排序(非递归,Java实现)

    归并排序(非递归):自底向上 public class MergeSort {/*** @param arr 待排序的数组* @param left 本次归并的左边界* @param mid 本次归并 ...

  6. C++非递归合并排序的通用实现算法(附完整源码)

    C++非递归合并排序的通用实现算法 C++非递归合并排序的通用实现算法完整源码(定义,实现,main函数测试) C++非递归合并排序的通用实现算法完整源码(定义,实现,main函数测试) #inclu ...

  7. 合并两个排序的链表递归和非递归C++实现

    题目描述: 输入两个单调递增的链表,输出两个链表合成后的链表,要求合成后的链表满足单调不减规则. 1.分析 已知输入的两个链表递增有序,要使输出的链表依然递增有序,可以依次从输入的两个链表中挑选最小的 ...

  8. 合并排序 非递归 java_合并排序-非递归

    #include //合并排序非递归--自然合并排序 typedef int Type; using namespace std; void Merge(Type c[],Type d[],int l ...

  9. 归并排序(递归实现+非递归实现+自然合并排序)

    归并排序的确是分治思想的经典代表.写了很多次,这次又有新的收获,过去用的是递归的实现方式,理论上任何用递归方法实现的代码都可以转换为非递归的形式,所以此例也不例外.然后再用非递归的实现方法上进行改进, ...

最新文章

  1. 有人从英伟达借了台DGX A100,让神经网络自己造了个GTA5自己玩
  2. HTML数字比较大小游戏,Javascript 比较两个数大小并输出最大数
  3. bootstrap使用总结
  4. 3d相册 html 代码_HTML5 3D立体图片相册
  5. Java并发编程之美读书笔记-并发编程基础2
  6. [JAVA基础类库] Objec类
  7. 安装包镜像_创建 macOS Catalina cdr格式镜像安装包
  8. python 程序运行插件_如何使Python插件在Pluma中运行?
  9. 缘起 Dubbo ,讲讲 Spring XML Schema 扩展机制
  10. mysql atlas php_Mysql中间件代理 Atlas
  11. 【Python】提升Python程序性能的好习惯
  12. 【今日CV 计算机视觉论文速览 第112期】Mon, 6 May 2019
  13. java stream 泛型
  14. java jmx 监控tomcat_jmx监控之Tomcat
  15. linux 代码行数统计利器 ——cloc
  16. 从“H1N1病毒”看危机意识的重要性
  17. 计算机网络线接法,网线水晶头接法图解 一分钟学会网线怎么接
  18. Unity 打开摄像头
  19. 华为RH2288v3安装系统
  20. 模式识别算法之2--感知器(感知机)算法

热门文章

  1. NYOJ 201 作业题
  2. NYOJ 661 亲亲串
  3. ORA-07445 [kxsxsi()+450] [SIGSEGV]
  4. 个人JS体系整理(二)
  5. SpringMVC+ZTree实现树形菜单权限配置
  6. 如何建立软件测试管理体系?
  7. Java学习:多线程(2)
  8. XML学习笔记(二)-- DTD格式规范
  9. SCU3033 Destroying a Painting(最小费用最大流)
  10. iOS 序列化与反序列化