目录

归并排序的基本思想:

递归算法:

递归算法的思路分析:

开辟数组的函数:

递归的函数:

非递归算法:

非递归的思路分析:

边界问题:

时间复杂度和空间复杂度分析:


归并排序的基本思想:

归并排序所采用的思想是将大问题分为小问题来处理,即我们常说的分治。

如图,要排序一个数组,我们就把这个数组往小的区间拆分,比如要排序数组,先排序左边数组,再排序右边数组,被分开的两个数组可以继续按照这种方式,继续分成更小的区间,直到每个区间只有一个数字的时候,再通过比较 相邻区间来插入到数组中,当然,这里插入的数组我们需要额外开辟。

归并排序的逻辑我们理清楚后,有两种方式来实现:递归和非递归,我们分别来介绍一下

递归算法:

递归算法的思路分析:

显然归并排序将大问题分治为小问题的思想是非常显然也是很适合用递归去解决的

写好递归逻辑的代码,由于我们需要开辟数组,因此我们将递归代码单独封装成另一个函数,在开辟数组的函数里调用就可以:

开辟数组的函数:

void MergeSort(int* a, int n)
{int* tmp = (int*)malloc(sizeof(int)*n);if (tmp == NULL){perror("malloc fail");exit(-1);}_MergeSort(a,0,n-1,tmp);free(tmp);tmp = NULL;
}

递归的函数:

void _MergeSort(int* a,int begin,int end,int* tmp)
{if (begin >= end){//如果开始大于等于结束,意味着只有一个数据,可以直接返回return;}int mid = (begin + end) / 2;//递归使子区间有序_MergeSort(a,begin, mid, tmp);_MergeSort(a,mid+1, end, tmp);//归并[begin,mid] [mid+1,end]int begin1 = begin, end1 = mid;int begin2 = mid + 1, end2 = end;int i = begin;//每个区间的i都是不同的,因此i要从begin开始while (begin1 <= end1 && begin2 <= end2){if (a[begin1] <= a[begin2]){tmp[i++] = a[begin1++];}else{tmp[i++] = a[begin2++];}}//如果某个区间还剩下数据,直接插入到tmp的后面while (begin1 <= end1){tmp[i++] = a[begin1++];}while (begin2 <= end2){tmp[i++] = a[begin2++];}memcpy(a+begin,tmp + begin,sizeof(int)*(end - begin + 1));
}

非递归算法:

非递归的思路分析:

归并排序的递归算法非常简单且容易理解,我们主要来看非递归算法:

非递归算法本质也是分治的思想,只不过在非递归算法这里,我们直接从一个一个的数据开始排序

第一次排序范围:0~0   1~1   2~2   3~3   4~4   5~5   6~6   7~7(每组一个)

第二次排序范围:0~1   2~3   3~4   4~5   5~6   6~7(每组两个)

第三次排序范围:0~3   4~7(每组四个)

int rangeN = 1;while (rangeN < n){for (int i = 0;i<n;i+=2*rangeN){int begin1 = i, end1 = i + rangeN - 1; int begin2 = i + rangeN, end2 = i + 2 * rangeN - 1;int j = i;while (begin1 <= end1 && begin2 <= end2){if (a[begin1] <= a[begin2]){tmp[j++] = a[begin1++];}else{tmp[j++] = a[begin2++];}}while (begin1 <= end1){tmp[j++] = a[begin1++];}while (begin2 <= end2){tmp[j++] = a[begin2++];}}memcpy(a,tmp,sizeof(int)*n);rangeN *= 2;}

边界问题:

弄清楚非递归是如何处理数据的,我们还需要考虑一个问题:边界问题

上述四个变量中,有三种越界的情况:end1越界,begin2越界,end2越界

针对上述问题,我们要修改对应的边界,防止越界:

void MergeSortNONR(int* a,int n)
{int* tmp = (int*)malloc(sizeof(int)*n);if (tmp == NULL){exit(-1);}int rangeN = 1;while (rangeN < n){for (int i = 0;i<n;i+=2*rangeN){int begin1 = i, end1 = i + rangeN - 1; int begin2 = i + rangeN, end2 = i + 2 * rangeN - 1;int j = i;if (end1 >= n){end1 = n - 1;begin2 = n;end2 = n - 1;}else if (begin2 >= n){begin2 = n;end2 = n - 1;}else if (end2 >= n){end2 = n - 1;}while (begin1 <= end1 && begin2 <= end2){if (a[begin1] <= a[begin2]){tmp[j++] = a[begin1++];}else{tmp[j++] = a[begin2++];}}while (begin1 <= end1){tmp[j++] = a[begin1++];}while (begin2 <= end2){tmp[j++] = a[begin2++];}}memcpy(a,tmp,sizeof(int)*n);rangeN *= 2;}
}

时间复杂度和空间复杂度分析:

时间复杂度:

归并排序的时间复杂度比较容易计算,由于是树的结构,时间复杂度是标准的O(nlogn)

空间复杂度:

由于归并排序需要开辟额外数组,所以空间复杂度是O(N),这也是归并排序的缺点


至此我们就讲解完成了递归方式和非递归方式实现归并排序,如果对你有所收获,还请点赞关注,我们下次再见

【排序】详细聊聊归并排序(含非递归)相关推荐

  1. 排序算法杂谈(三) —— 归并排序的非递归实现

    1. 递归 在众多排序算法中,归并排序(Merge Sort)和快速排序(Quick Sort)都是时间复杂度为 O(nlog2n) 的高效排序. 这两种排序有一种共性,就是运用到了递归的思想. 在程 ...

  2. 【Java】 归并排序的非递归实现

    归并排序可以采用递归方法(见:归并排序),但递归方法会消耗深度位O(longn)的栈空间,使用归并排序时,应该尽量使用非递归方法.本文实现了java版的非递归归并排序. 更多:数据结构与算法合集 思路 ...

  3. 计算机算法设计与分析 递归实现归并排序和非递归实现归并排序

    递归实现归并排序 输入排序的数组的长度,程序用随机数生成对应的数组,之后递归调用排序,排序过程中思想类似于二分,当每个最小分组中只有一个元素时开始返回,直到排完整个数组. #include<bi ...

  4. 手把手教你归并排序(非递归)

    今天,我们一起来学习归并排序的非递归算法吧! 目录 一.优势 二.实现原理 三.代码实现 一.优势 相比于递归算法,归并排序的非递归算法不用多次调用同一个函数,不会向递归算法一样因为函数嵌套调用次数太 ...

  5. 二叉树递归(含非递归)遍历方式的流程图!

    时间紧迫,画的有点粗糙,请见谅! 创建 递归前序遍历 递归中序遍历 递归后序遍历 非递归前序+度为2.1叶子结点等求法

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

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

  7. 归并排序详解(递归+非递归)

    目录 归并排序 归并排序的思想 递归实现 非递归实现 归并排序 归并排序和之前讲的快速排序.希尔排序.堆排序一样,时间复杂度是O(N*logN). 它相对难理解一点,接下来我就从递归以及非递归两个方面 ...

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

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

  9. 归并排序(递归+非递归)

    目录 一.什么是归并排序? 二.归并排序(递归) 三.归并排序(非递归) 一.什么是归并排序? 归并排序,是创建在归并操作上的一种有效的排序算法.算法是采用分治法(Divide and Conquer ...

最新文章

  1. 设计模式之Prototype(原型)(转)
  2. spring基于注解的IOC以及IoC的案例——概念
  3. 使用jdk 自带的jarsigner 签名工具签名的apk 和android规范有区别
  4. guid主分区表损坏怎么办_抹盘提示进程失败,导致Win 10无法正常开机怎么办?...
  5. 【PHP】网站防止QQ拦截防红跳转代码
  6. HTML:调用静态页面html 的几种方法
  7. php-fpm的安装与测试
  8. Navigation + Tab Bar 常用组合框架
  9. 情感分析入门[2]-一些数学工具
  10. 登录,注册,个人信息,退出的隐藏和出现
  11. android屏幕内容实时传输,在设备之间无缝传输内容
  12. 最好用音频剪辑的软件,使用方法?
  13. python——Matplotlib饼图、直方图的绘制
  14. BZOJ1023 [SHOI2008]仙人掌图
  15. 微信输入法 你有了吗?张小龙:防窃听、护隐私
  16. word2计算机fx公式,Word的fx公式怎么使用说明
  17. JAVA String时间转化为数据库Date类型
  18. 关于Linux操作系统的处理机管理分析
  19. 系统架构设计-安全篇
  20. Linux(Ubuntu) 修复grub引导向

热门文章

  1. c语言switch不可以用枚举,76-枚举类型用在switch中
  2. python 股票交易接口 github_GitHub - zhaoneng/vnpy: 基于python的开源交易平台开发框架...
  3. 集合框架学习笔记:Collection体系和Map体系、Collections工具类
  4. 机械臂C语言编程,ROS下C++控制UR机械臂
  5. 如何建立Multi-Step(多步预测)的LSTM时间序列模型(以对家庭用电预测为例)
  6. java程序设计基础实验_JAVA程序设计基础实验1.doc
  7. Big Faceless:PDF Viewer for JAVA Crack
  8. 基于android的检测心率,基于Android系统的心率信息监测软件的研究与实现
  9. Magento2.1x常见报错处理
  10. python tushare量化股票大数据分析整合版