【数据结构】归并排序 的递归实现与非递归实现
归并排序
- 前言
- 一、归并排序递归实现
- (1)归并排序的核心思路
- (2)归并排序实现的核心步骤
- (3)归并排序码源详解
- (4)归并排序效率分析
- 1)时间复杂度 O(N*logN)
- 2)空间复杂度 O(N)
- 稳定性:稳定
- 二、归并排序的非递归实现
- (1) 关于递归的缺点的讨论
- (2) 归并排序 非递归算法实现思路
- (3)码源详解
- (4)运行结果
前言
快速排序:前序
归并排序:后序
一、归并排序递归实现
(1)归并排序的核心思路
将 已有序的子序列 合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。
(2)归并排序实现的核心步骤
- 向下递归 对半分割 。
- 递归返回条件:递归到最小,1个即是有序 [ 控制的是范围 归并的区间 ]
- 递归到最深处,最小时,就递归回去,开始分按对半分割分出的范围, 将 已有序的子序列 合并,在 tmp 里进行归并。
- 再将tmp里形成的有序序列,拷贝回原数组 【 因为下一次递归回去上一层再进行下一次的归并过程中,会将数据在tmp中进行归并,会将tmp中的数据覆盖,所以要及时将小部分已归并排好序的子序列拷贝回原数组 】
- 再进行递归返回上一层的递归归并,直到递归层数都结束。
(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换到8的位置后,8和7就不再保持有序了。
稳定性:稳定
二、归并排序的非递归实现
归并排序是 二分的思想 => logN层 => 递归不会太深、且现编译器优化后,递归、非递归的性能差距没有那么大了 =>所以不用考虑非递归,但非递归实现也不难。下面带大家简单实现一下。
(1) 关于递归的缺点的讨论
递归的缺点:递归消耗栈帧,递归的层数太深,容易爆栈。
【栈的空间比较小,在x86(32位)环境下,只有8M。(对比同一环境下的堆,则有2G+)。因为平时函数调用开不了多少个栈帧。理论上递归深度>1w 可能就会爆 ,但实际上5k左右就爆掉了】
这时就需要改非递归了
★递归—>非递归:
- 改循环
- 利用 [ 数据结构 ] 栈(本质上是通过malloc 在堆上开辟的内存空间,内存空间足够大)
- 递归逆着来求(如斐波那契数列逆着来求也是可行的)【归并排序的非递归实现 也是个很好的例子】
而归并排序的非递归实现则是用到了其中的第三点 。
(2) 归并排序 非递归算法实现思路
虽说不是递归,是递归的逆序版。是直接从最深层次,逆序回去,直接开始归并排序,免去了向下深入递归。虽说不是递归,但也算是递归的思路的另一个种实现。
- 开辟新的数组(临时存放)用于归并排序过程
- int gap=1;gap*=2【gap控制归并的范围:11归并,22归并,44归并】
- for (int i = 0; i < n; i += 2 * gap) { 【i 控制进行比较轮到的组号,控制进行归并的组号】
- 归并完一轮,将归并好的有序数组拷贝回原数组memcpy 。
- 进入新的一轮归并,直至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)运行结果
相关文章:

【数据结构】归并排序 的递归实现与非递归实现
归并排序 前言一、归并排序递归实现(1)归并排序的核心思路(2)归并排序实现的核心步骤(3)归并排序码源详解(4)归并排序效率分析1)时间复杂度 O(N*logN…...

Go的命令行工具开发:使用Cobra库
今天我们将深入探讨如何使用Go语言和Cobra库来开发命令行工具。 命令行工具在软件开发中有着广泛的应用,它们快速、高效,且易于自动化。 Go语言因其简洁、高效而被广泛用于命令行工具的开发。Cobra库则是Go中用于构建命令行工具的重要库之一。 为什么选…...

坚持#第420天~阿里云轻量服务器内存受AliYunDunMonito影响占用解决方法
阿里云轻量服务器内存受AliYunDunMonito影响占用解决方法,亲测有效: Mobax好卡啊,那就直接在阿里云后台操作即可,阿里云后台也可以上传文件。 Navicat mysql好卡啊,那就直接在阿里云后台最上面帮助的右边有个数据库&…...

时间序列聚类的直观方法
一、介绍 我们将使用轮廓分数和一些距离度量来执行时间序列聚类实验,同时利用直观的可视化,让我们看看下面的时间序列: 这些可以被视为具有正弦、余弦、方波和锯齿波的四种不同的周期性时间序列 如果我们添加随机噪声和距原点的距离来沿 y 轴…...
vue3的reactive源码解析
reactive源码解析 总结一句: reactive是个函数。reactive函数返回了一个createReactiveObject函数,createReactiveObject又返回了一个“经new Proxy实例化”的对象。 详细介绍: 我们使用时传给reactive函数一个对象类型target,reactive又将target传给cr…...

【ElasticSearch系列-04】ElasticSearch的聚合查询操作
ElasticSearch系列整体栏目 内容链接地址【一】ElasticSearch下载和安装https://zhenghuisheng.blog.csdn.net/article/details/129260827【二】ElasticSearch概念和基本操作https://blog.csdn.net/zhenghuishengq/article/details/134121631【三】ElasticSearch的高级查询Quer…...
Redisson初始
最近的自己,一直都在做些老年的技术,没有啥升级,自己也快麻木了,自己该怎么说,那必须行动起来啊!~来来,我们一起增长自己的内功 分布式锁的最强实现: Redisson 1.概念 在介绍之前,我们要知道这个Redisson是啥? 难道就是Redis的son?(我第一次就这么认为的哈哈!) 事实也的确如…...

【华为OD题库-018】AI面板识别-Java
题目 Al识别到面板上有N(1<N≤100)个指示灯,灯大小一样,任意两个之间无重叠。由于AI识别误差,每次识别到的指示灯位置可能有差异,以4个坐标值描述Al识别的指示灯的大小和位置(左上角x1,y1,右下角x2.y2)。请输出先行…...

[概述] 点云滤波器
拓扑结构 点云是一种三维数据,有几种方法可以描述其空间结构,以利于展开搜索 https://blog.csdn.net/weixin_45824067/article/details/131317939 KD树 头文件:pcl/kdtree/kdtree_flann.h 函数:pcl::KdTreeFLANN 作用:…...
[笔记] 汉字判断
参考博客:如果判断一个字符是西文字符还是中文字符 结论: 汉字转数字后,会占两位字符位,两位都是负数。 参考下面代码 输入:你 输出:01 #include<bits/stdc.h> using namespace std; int main() {cha…...

Android开发笔记(三)—Activity篇
活动组件Activity 启动和结束生命周期启动模式信息传递Intent显式Intent隐式Intent 向下一个Activity发送数据向上一个Activity返回数据 附加信息利用资源文件配置字符串利用元数据传递配置信息给应用页面注册快捷方式 启动和结束 (1)从当前页面跳到新页…...

nodejs+vue+python+php在线购票系统的设计与实现-毕业设计
伴随着信息时代的到来,以及不断发展起来的微电子技术,这些都为在线购票带来了很好的发展条件。同时,在线购票的范围不断增大,这就需要有一种既能使用又能使用的、便于使用的、便于使用的系统来对其进行管理。在目前这种大环境下&a…...

基于Taro + React 实现微信小程序半圆滑块组件、半圆进度条、弧形进度条、半圆滑行轨道(附源码)
效果: 功能点: 1、四个档位 2、可点击加减切换档位 3、可以点击区域切换档位 4、可以滑动切换档位 目的: 给大家提供一些实现思路,找了一圈,一些文章基本不能直接用,错漏百出,代码还藏着掖…...

城市内涝解决方案:实时监测,提前预警,让城市更安全
城市内涝积水问题是指城市地区在短时间内遭遇强降雨后,地面积水过多,导致城市交通堵塞、居民生活不便、财产损失等问题。近年来,随着全球气候变化和城市化进程的加速,城市内涝积水问题越来越突出,成为城市发展中的一大…...

编译正点原子LINUXB报错make: arm-linux-gnueabihf-gcc:命令未找到
编译正点原子LINUX报错make: arm-linux-gnueabihf-gcc:命令未找到 1.报错内容2.解决办法3./bin/sh: 1: lzop: not found4.编译成功 1.报错内容 make: arm-linux-gnueabihf-gcc:命令未找到CHK include/config/kernel.releaseCHK include/generat…...

工地现场智慧管理信息化解决方案 智慧工地源码
智慧工地系统充分利用计算机技术、互联网、物联网、云计算、大数据等新一代信息技术,以PC端,移动端,设备端三位一体的管控方式为企业现场工程管理提供了先进的技术手段。让劳务、设备、物料、安全、环境、能源、资料、计划、质量、视频监控等…...

Javaweb之HTML,CSS的详细解析
2. HTML & CSS 1). 什么是HTML ? HTML: HyperText Markup Language,超文本标记语言。 超文本:超越了文本的限制,比普通文本更强大。除了文字信息,还可以定义图片、音频、视频等内容。 标记语言:由标签构成的语言…...

基于python+django+vue开发的酒店预订管理系统 - 毕业设计 - 课程设计
文章目录 源码下载地址项目介绍项目功能界面预览项目备注毕设定制,咨询 源码下载地址 点击这里下载源码 项目介绍 该系统是基于pythondjango开发的酒店预定管理系统。适用场景:大学生、课程作业、毕业设计。学习过程中,如遇问题可在github…...

使用vscode实现远程开发,并通过内网穿透在公网环境下远程连接
文章目录 前言1、安装OpenSSH2、vscode配置ssh3. 局域网测试连接远程服务器4. 公网远程连接4.1 ubuntu安装cpolar内网穿透4.2 创建隧道映射4.3 测试公网远程连接 5. 配置固定TCP端口地址5.1 保留一个固定TCP端口地址5.2 配置固定TCP端口地址5.3 测试固定公网地址远程 前言 远程…...
ArrayList集合2
ArrayList集合的一些方法 ⑥chear()从列表中移除所有元素 ⑦.isEmpty()判断列表中是否包含元素,不包含返回true,否则返回false public class Test{public static void main(String[] args){Arraylist<String> list new Arraylist<String>()…...

eNSP-Cloud(实现本地电脑与eNSP内设备之间通信)
说明: 想象一下,你正在用eNSP搭建一个虚拟的网络世界,里面有虚拟的路由器、交换机、电脑(PC)等等。这些设备都在你的电脑里面“运行”,它们之间可以互相通信,就像一个封闭的小王国。 但是&#…...

基于FPGA的PID算法学习———实现PID比例控制算法
基于FPGA的PID算法学习 前言一、PID算法分析二、PID仿真分析1. PID代码2.PI代码3.P代码4.顶层5.测试文件6.仿真波形 总结 前言 学习内容:参考网站: PID算法控制 PID即:Proportional(比例)、Integral(积分&…...

无法与IP建立连接,未能下载VSCode服务器
如题,在远程连接服务器的时候突然遇到了这个提示。 查阅了一圈,发现是VSCode版本自动更新惹的祸!!! 在VSCode的帮助->关于这里发现前几天VSCode自动更新了,我的版本号变成了1.100.3 才导致了远程连接出…...
基础测试工具使用经验
背景 vtune,perf, nsight system等基础测试工具,都是用过的,但是没有记录,都逐渐忘了。所以写这篇博客总结记录一下,只要以后发现新的用法,就记得来编辑补充一下 perf 比较基础的用法: 先改这…...

用docker来安装部署freeswitch记录
今天刚才测试一个callcenter的项目,所以尝试安装freeswitch 1、使用轩辕镜像 - 中国开发者首选的专业 Docker 镜像加速服务平台 编辑下面/etc/docker/daemon.json文件为 {"registry-mirrors": ["https://docker.xuanyuan.me"] }同时可以进入轩…...

如何理解 IP 数据报中的 TTL?
目录 前言理解 前言 面试灵魂一问:说说对 IP 数据报中 TTL 的理解?我们都知道,IP 数据报由首部和数据两部分组成,首部又分为两部分:固定部分和可变部分,共占 20 字节,而即将讨论的 TTL 就位于首…...
Web中间件--tomcat学习
Web中间件–tomcat Java虚拟机详解 什么是JAVA虚拟机 Java虚拟机是一个抽象的计算机,它可以执行Java字节码。Java虚拟机是Java平台的一部分,Java平台由Java语言、Java API和Java虚拟机组成。Java虚拟机的主要作用是将Java字节码转换为机器代码&#x…...

[ACTF2020 新生赛]Include 1(php://filter伪协议)
题目 做法 启动靶机,点进去 点进去 查看URL,有 ?fileflag.php说明存在文件包含,原理是php://filter 协议 当它与包含函数结合时,php://filter流会被当作php文件执行。 用php://filter加编码,能让PHP把文件内容…...

DBLP数据库是什么?
DBLP(Digital Bibliography & Library Project)Computer Science Bibliography是全球著名的计算机科学出版物的开放书目数据库。DBLP所收录的期刊和会议论文质量较高,数据库文献更新速度很快,很好地反映了国际计算机科学学术研…...

消防一体化安全管控平台:构建消防“一张图”和APP统一管理
在城市的某个角落,一场突如其来的火灾打破了平静。熊熊烈火迅速蔓延,滚滚浓烟弥漫开来,周围群众的生命财产安全受到严重威胁。就在这千钧一发之际,消防救援队伍迅速行动,而豪越科技消防一体化安全管控平台构建的消防“…...