【数据结构】归并排序 的递归实现与非递归实现

归并排序

  • 前言
  • 一、归并排序递归实现
    • (1)归并排序的核心思路
    • (2)归并排序实现的核心步骤
    • (3)归并排序码源详解
    • (4)归并排序效率分析
      • 1)时间复杂度 O(N*logN)
      • 2)空间复杂度 O(N)
      • 稳定性:稳定
  • 二、归并排序的非递归实现
    • (1) 关于递归的缺点的讨论
  • (2) 归并排序 非递归算法实现思路
  • (3)码源详解
  • (4)运行结果



前言

快速排序:前序
归并排序:后序



一、归并排序递归实现

(1)归并排序的核心思路

将 已有序的子序列 合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。


(2)归并排序实现的核心步骤

在这里插入图片描述

  1. 向下递归 对半分割
  2. 递归返回条件:递归到最小,1个即是有序 [ 控制的是范围 归并的区间 ]
  3. 递归到最深处,最小时,就递归回去,开始分按对半分割分出的范围, 将 已有序的子序列 合并,在 tmp 里进行归并。
  4. 将tmp里形成的有序序列,拷贝回原数组 【 因为下一次递归回去上一层再进行下一次的归并过程中,会将数据在tmp中进行归并,会将tmp中的数据覆盖,所以要及时将小部分已归并排好序的子序列拷贝回原数组 】
  5. 再进行递归返回上一层的递归归并,直到递归层数都结束。


(3)归并排序码源详解

void _MergeSort(DataType* a, DataType* tmp, int begin, int end) {if (begin>=end) {      //递归返回的条件:不正常的的范围 或 只剩1个数return;}int mid = (begin + end) / 2;//先递归到最小//[begin,mid][mid+1,end]_MergeSort(a, tmp, begin, mid);    //数组是从0开始,所以end=mid-1这样设计_MergeSort(a, tmp, mid+1, end);//再进行归并 —— 所以归并的过程,设计在递归后面(后序)//归并到tmp数组,再拷贝回去int begin1 = begin; int end1 = mid;int begin2 = mid + 1; int end2 = end;int index = begin;       //指向tmp,=begin是 根据要进行比较插入的数组的位置 找到其对应在tmp中所对应的位置,则不会覆盖前面已经排好序的数据//原型:合并两组数,且有序while (begin1 <= end1 && begin2 <= end2) {      //&&其中一组满足了条件就不再继续,就跳出循环if (a[begin1] < a[begin2]) {tmp[index++] = a[begin1++];}else {tmp[index++] = a[begin2++];}}	while (begin1 <= end1) {       //把剩下还没插入tmp的,插入进去tmp[index++] = a[begin1++];}while (begin2 <= end2) {       //把剩下还没插入tmp的,插入进去tmp[index++] = a[begin2++];}//拷贝回原数组//source   dest     拷贝的数组大小memcpy(a+begin,tmp+begin,sizeof(DataType)*(end-begin+1));
}void MergeSort(DataType* a, int n) {DataType* tmp = (DataType*)malloc(sizeof(DataType) * n);     //开辟新的数组(临时存放)用于归并排序过程if (tmp == NULL) {perror("malloc fail");return;}//将 待排序的数组、归并过程用的tmp临时数组、归并范围 传过去_MergeSort(a, tmp, 0, n - 1);         //因为 主函数中有malloc tmp的操作,若递归调用主函数,则每次调用都会malloc,再free 是对空间上的浪费//因此用子函数进行递归 【_子函数】free(tmp);
}


(4)归并排序效率分析

1)时间复杂度 O(N*logN)

二分 有 logN 层 ,也正是因为是logN层,递归深度不会太深,所以不用考虑非递归,当然非递归也能实现。
每层有N个数进行归并排序

=>N*logN
在这里插入图片描述

2)空间复杂度 O(N)

开辟了个 空间大小为N的 新的数组,用于归并有序的过程。
在这里插入图片描述
在原数组上归并会出现什么问题:

  1. 会覆盖数据
  2. 最小的1换到8的位置后,8和7就不再保持有序了。

稳定性:稳定



二、归并排序的非递归实现

归并排序是 二分的思想 => logN层 => 递归不会太深、且现编译器优化后,递归、非递归的性能差距没有那么大了 =>所以不用考虑非递归,但非递归实现也不难。下面带大家简单实现一下。

(1) 关于递归的缺点的讨论

递归的缺点:递归消耗栈帧,递归的层数太深,容易爆栈。
【栈的空间比较小,在x86(32位)环境下,只有8M。(对比同一环境下的堆,则有2G+)。因为平时函数调用开不了多少个栈帧。理论上递归深度>1w 可能就会爆 ,但实际上5k左右就爆掉了】

这时就需要改非递归了

★递归—>非递归

  1. 改循环
  2. 利用 [ 数据结构 ] 栈(本质上是通过malloc 在堆上开辟的内存空间,内存空间足够大)
  3. 递归逆着来求(如斐波那契数列逆着来求也是可行的)【归并排序的非递归实现 也是个很好的例子】
    在这里插入图片描述而归并排序的非递归实现则是用到了其中的第三点 。


(2) 归并排序 非递归算法实现思路

虽说不是递归,是递归的逆序版。是直接从最深层次,逆序回去,直接开始归并排序,免去了向下深入递归。虽说不是递归,但也算是递归的思路的另一个种实现。
在这里插入图片描述

  1. 开辟新的数组(临时存放)用于归并排序过程
  2. int gap=1;gap*=2【gap控制归并的范围:11归并,22归并,44归并】
  3. for (int i = 0; i < n; i += 2 * gap) { 【i 控制进行比较轮到的组号,控制进行归并的组号】
  4. 归并完一轮,将归并好的有序数组拷贝回原数组memcpy 。
  5. 进入新的一轮归并,直至gap>n则归并完成

☆注意的两个情况
6. if (begin2 >= n) { break; } 第二组不存在,这一组不用归并了
7. if (end2 > n) { end2 = n - 1; } 第二组右边界越界问题,修正一下
在这里插入图片描述



(3)码源详解

//归并排序——非递归版 :从最底层开始,逆着往回求(如同斐波那契)
void MergeSortNonR(DataType* a,int n) {DataType* tmp = (DataType*)malloc(sizeof(DataType) * n);     //开辟新的数组(临时存放)用于归并排序过程if (tmp == NULL) {perror("malloc fail");return;}int gap = 1;while (gap < n) {                                        //gap控制 11归并,22归并,44归并//i控制进行比较轮到的组号,控制归并的组号for (int i = 0; i < n; i += 2 * gap) {               //可以通过画出数组,在草稿纸上演示,理清要比较的数begin1、end1、begin2、end2之间和i、gap的数量关系//[begin1,end1][begin2,end2]归并               int begin1 = i; int end1 = i + gap - 1;          //-1 控制下标的边界int begin2 = i + gap; int end2 = i + 2 * gap - 1;//如果第二组不存在,这一组不用归并了if (begin2 >= n) {break;}//第二组右边界越界问题,修正一下if (end2 > n) {end2 = n - 1;}int index = i;while (begin1 <= end1 && begin2 <= end2) {           //&& 其中一组不满足了条件了(其中一个数组遍历完了)就不再继续,就跳出循环  if (a[begin1] < a[begin2]) {                     //两个数组比对,小的放进去tmp[index++] = a[begin1++];}else{tmp[index++] = a[begin2++];}}while (begin1 <= end1) {                         //把剩下的没遍历进去的数组剩余的部分 遍历进去tmp[index++] = a[begin1++];}while (begin2 <= end2) {tmp[index++] = a[begin2++];}//拷贝回原数组//通过a+i、tmp+i来找到要拷贝数组部分的对应下标memcpy(a + i, tmp + i,(end2-i+1)*sizeof(DataType));}printf("\n");gap *= 2;                                            //gap控制总体归并}free(tmp);
}


(4)运行结果

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/129545.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

matlab双目标定中基线物理长度获取

在MATLAB进行双目摄像机标定时,通常会获得相机的内参,其中包括像素单位的焦距(focal length)以及物理单位的基线长度(baseline)。对于应用中的深度估计和测量,基线长度的物理单位非常重要,因为它直接影响到深度信息的准确性。有时候,您可能只能获取像素单位的焦距和棋…

FPGA 如何 固化程序到 FLASH中

1、导出Hardware 2、导出bit文件 3、打开SDK 4、 点击Ok 5、创建工程 6、 输入工程名称&#xff1a;guhua 7、选择 Zynq FSBL 8、单击 guhua、然后点击 build 点击&#xff1a;build all 9、 右键之后&#xff0c;点击&#xff1a;Creat Boot Image 10、点击 Cr…

水库大坝可视化智能远程监管方案,助力安全监测智能巡检

一、背景需求 水库大坝作为防洪度汛的重要设施&#xff0c;其安全问题直接关系到人民群众的生命财产安全。因此&#xff0c;必须加强对大坝水库的安全管理&#xff0c;对水库除险加固和运行管护要消除存量隐患&#xff0c;实现常态化管理&#xff0c;同时要配套完善重点小型水…

Leetcode—485.最大连续1的个数【简单】

2023每日刷题&#xff08;十五&#xff09; Leetcode—485.最大连续1的个数 实现代码 int findMaxConsecutiveOnes(int* nums, int numsSize){int max 0;int i;int flag 0;int cnt 0;for(i 0; i < numsSize; i) {if(nums[i] 1) {if(flag 0) {flag 1;cnt 1;} else {…

ARM 版 OpenEuler 22.03 部署 KubeSphere v3.4.0 不完全指南续篇

作者&#xff1a;运维有术 前言 知识点 定级&#xff1a;入门级KubeKey 安装部署 ARM 版 KubeSphere 和 KubernetesARM 版 KubeSphere 和 Kubernetes 常见问题 实战服务器配置 (个人云上测试服务器) 主机名IPCPU内存系统盘数据盘用途ks-master-1172.16.33.1661650200KubeSp…

3D人像手办定制业务再掀热潮,这一次有怎样的革新?(方法篇)

最近&#xff0c;3D真人手办热潮再起&#xff0c;最出圈的一次当属亚运会的3D打印元宇宙体验舱里面各国运动员带火的真人手办定制项目。作为3D技术推广者&#xff0c;博雅仔也在后台接受了很多朋友的询问—— ◆ 技术已经成熟了吗&#xff1f; ◆ 个人定做3D真人手办市场价格…

百度百科怎么创建?百科创建需要注意哪些(一文看懂品牌/企业/人物百科创建)

随着互联网的不断发展&#xff0c;许多企业或品牌都选择创建百度百科作为一种很好的展示方式。百度百科可以被视为一张网络名片&#xff0c;拥有它能够提高人物、企业、品牌的知名度和影响力。那么人物百科、企业百科、品牌百科到底怎么创建呢&#xff1f; 大家创建百科前建议先…

3.4_Linux-浏览文件系统

1.Linux 文件系统 如果你刚接触Linux系统&#xff0c;可能就很难弄清楚Linux如何引用文件和目录&#xff0c;对已经习惯Microsoft Windows操作系统方式的人来说更是如此。在继续探索Linux系统之前&#xff0c;先了解一下它的布局是有好处的。 你将注意到的第一个不同点是&…

玻色量子“天工量子大脑”亮相中关村论坛,大放异彩

2023年5月25日至30日&#xff0c;2023中关村论坛&#xff08;科博会&#xff09;在北京盛大召开。中关村论坛&#xff08;科博会&#xff09;是面向全球科技创新交流合作的国家级平台行业盛会&#xff0c;由科技部、国家发展改革委、工业和信息化部、国务院国资委、中国科学院、…

数据结构——顺序表(SeqList)

目录 1. 顺序表介绍 2. 顺序表工程 2.1 顺序表定义 2.1.1 静态顺序表 2.1.2 动态顺序表 2.2顺序表接口 2.2.1 顺序表初始化 2.2.2 顺序表打印 2.2.3 顺序表销毁 2.2.4 顺序表数据插入 2.2.4.1 容量检查 2.2.4.2 顺序表尾插 2.2.4.3 顺序表头插 2.2.4.4 顺序表随机…

人工智能与卫星:颠覆性技术融合开启太空新时代

人工智能与卫星&#xff1a;颠覆性技术融合开启太空新时代 摘要&#xff1a;本文将探讨人工智能与卫星技术的融合&#xff0c;并介绍其应用、发展和挑战。通过深入了解这一领域的前沿动态&#xff0c;我们将展望一个由智能卫星驱动的未来太空时代。 一、引言 近年来&#xf…

lazada商品评论API接口(评论内容|日期|买家昵称|追评内容|评论图片|评论视频..)

Lazada商品评论API接口是Lazada开放平台提供的一种API接口&#xff0c;可以帮助开发者获取Lazada平台上的商品评论数据。 通过该接口&#xff0c;开发者可以获取到用户对商品的评论信息&#xff0c;包括评论内容、评价等级、评论时间等&#xff0c;从而了解用户对商品的反馈和…

微信小程序overflow-x超出部分样式不渲染

把display:flex改成display:inline-flex&#xff0c; 将对象作为内联块级弹性伸缩盒显示&#xff0c; 类似与是子元素将父元素撑开&#xff0c;样式就显示出来了

智信SMS客户端操作手册

登录系统 登录前需查看用户协议输入账号和密码&#xff0c;点击登录即可 首页介绍 登录成功之后&#xff0c;进入操作页面。 操作页面包括&#xff1a;设置、导入手机号、发送短信三大块功能。 如图&#xff1a; 导入手机号 点击首页中的导入按钮&#xff0c;即可导入手机…

微服务框架SpringcloudAlibaba+Nacos集成RabbitMQ

目前公司使用jeepluscloud版本&#xff0c;这个版本没有集成消息队列&#xff0c;这里记录一下&#xff0c;集成的过程&#xff1b;这个框架跟ruoyi的那个微服务版本结构一模一样&#xff0c;所以也可以快速上手。 1.项目结构图&#xff1a; 配置类的东西做成一个公共的模块 …

医疗数据可视化大屏:重构医疗决策的未来

医疗行业一直是信息密集型领域之一&#xff0c;它的复杂性不仅在于患者病历和医疗数据的海量积累&#xff0c;还包括了病情诊断、医疗资源分配、病患治疗等多层次的挑战。随着信息技术的不断发展&#xff0c;医疗数据可视化大屏成为了一种创新性的工具&#xff0c;它为医疗管理…

阿里云推出AI编程工具“通义灵码“;生成式 AI 入门教程 2

&#x1f989; AI新闻 &#x1f680; 阿里云推出AI编程工具"通义灵码"&#xff0c;支持多种语言及实时续写功能 摘要&#xff1a;阿里云推出了一款名为"通义灵码"的AI编程工具&#xff0c;支持多种主流编程语言&#xff0c;包括Java、Python、Go等。该工…

java spring boot 注解、接口和问题解决方法(持续更新)

注解 RestController 是SpringMVC框架中的一个注解&#xff0c;它结合了Controller和ResponseBody两个注解的功能&#xff0c;用于标记一个类或者方法&#xff0c;表示该类或方法用于处理HTTP请求&#xff0c;并将响应的结果直接返回给客户端&#xff0c;而不需要进行视图渲染…

IDEA 设置代码注释模板

功能简介&#xff1a; 每次看别人代码时&#xff0c;面对毫无注释的类&#xff0c;除了头大还是头大&#xff0c; 以下提供了一种代码类注释模板 新建java类的时候&#xff0c;自动增加类注释&#xff0c;养成代码开发好习惯 效果展示&#xff1a; 代码模板&#xff1a; #if (…

css——半圆实心

案例 代码 <view class"circleBox"></view>.circleBox {width: 50px;height: 100px;background: red;border-radius: 100px 0 0 100px; }