C语言分析基础排序算法——归并排序

目录

归并排序

递归版本

非递归版本

非递归版本的问题

归并排序小优化


归并排序

归并排序,分为分治以及合并,分治部分可以使用递归或者非递归完成,归并排序的基本思路是:将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并

递归版本

递归版本的归并排序思路如下:先将数组分为不可再分割的只有一个数据的部分,再取小的部分进行尾插,每排序一次就将排序好的数据拷贝到原来的数组中

//以下面的数组为例
int data[] = { 10,5,6,9,1,3,4,7 };

void _MergeSort(int* data, int* tmp, int left, int right)
{//确定递归结束条件if (left == right){return;}//分割数组,首先确定当前数组的中间位置int mid = (left + right) / 2;_MergeSort(data, tmp, left, mid);_MergeSort(data, tmp, mid + 1, right);//取小的数值尾插到tmp数组中int begin1 = left;int end1 = mid;int begin2 = mid + 1;int end2 = right;int i = left;while (begin1 <= end1 && begin2 <= end2){if (data[begin1] < data[begin2]){tmp[i++] = data[begin1++];}else{tmp[i++] = data[begin2++];}}//存在一个数组先走完的情况while (begin1 <= end1){tmp[i++] = data[begin1++];}while (begin2 <= end2){tmp[i++] = data[begin2++];}//排序完之后将tmp数组中的数据拷贝回原来的数组memcpy(data + left, tmp + left, sizeof(int) * (right - left + 1));
}//归并排序递归版
void MergeSort(int* data, int sz)
{//因为需要将排序好的数据重新拷贝到原来的数组中,所以需要开辟数组int* tmp = (int*)malloc(sizeof(int) * sz);assert(tmp);//防止主函数递归导致每次都会重新开辟空间,所以使用子函数_MergeSort(data, tmp, 0, sz - 1);free(tmp);
}

非递归版本

在归并排序中,不使用递归版本时,需要考虑如何对数据进行分堆以及区间的控制,基本思路如下:在循环中,排序间隔为gap的部分数值,再改变gap值,重复前面的步骤,直到最后排序完成。具体思路如下:

//以下面的数组为例
int data[] = { 10,5,6,9,1,3,4,7 };

//归并排序非递归版本
void MergeSort_NotRecursion(int* data, int sz)
{//因为需要将排序好的数据重新拷贝到原来的数组中,所以需要开辟数组int* tmp = (int*)malloc(sizeof(int) * sz);assert(tmp);//开始间隔为1int gap = 1;while (gap < sz){//注意i每一次更新为两倍的gap,因为gap只是代表一组有多少个数据,需要i找到下一组for (int i = 0; i < sz; i += 2 * gap){int begin1 = i;int end1 = i + gap - 1;int begin2 = i + gap;int end2 = i + 2 * gap - 1;int j = begin1;while (begin1 <= end1 && begin2 <= end2){if (data[begin1] < data[begin2]) {tmp[j++] = data[begin1++];}else{tmp[j++] = data[begin2++];}}while (begin1 <= end1){tmp[j++] = data[begin1++];}while (begin2 <= end2){tmp[j++] = data[begin2++];}}memcpy(data, tmp, sizeof(int) * sz);gap *= 2;}free(tmp);
}

非递归版本的问题

但是上面的方法存在一个问题,如果数组的数据不是2的次方个,那么将无法完成排序,存在越界问题

//下面是当数组数据为9个时的越界情况
[0, 0] [1 1]
[2, 2] [3 3]
[4, 4] [5 5]
[6, 6] [7 7]
[8, 8] [9 9][0, 1] [2 3]
[4, 5] [6 7]
[8, 9] [10 11][0, 3] [4 7]
[8, 11] [12 15][0, 7] [8 15]

越界的情况分为三种:

  1. end1begin2end2越界,例如[8, 11]、[12, 15]
  2. begin2end2越界,例如[10, 11]
  3. end2越界,例如[8, 15]

对于上面的问题可以考虑对边界进行修正

第一种解决方法:

  1. begin2end1越界时,跳出循环不进行后方数据的调整
  2. end2越界时,修正end2为数组最后一个元素的位置

//归并排序非递归版本
void MergeSort_NotRecursion(int* data, int sz)
{//因为需要将排序好的数据重新拷贝到原来的数组中,所以需要开辟数组int* tmp = (int*)malloc(sizeof(int) * sz);assert(tmp);//开始间隔为1int gap = 1;while (gap < sz){    //注意i每一次更新为两倍的gap,因为gap只是代表一组有多少个数据,需要i找到下一组for (int i = 0; i < sz; i += 2 * gap){int begin1 = i;int end1 = i + gap - 1;int begin2 = i + gap;int end2 = i + 2 * gap - 1;int j = begin1;if (begin2 >= sz || end1 >= sz){break;}if (end2 >= sz){end2 = sz - 1;}while (begin1 <= end1 && begin2 <= end2){if (data[begin1] < data[begin2]) {tmp[j++] = data[begin1++];}else{tmp[j++] = data[begin2++];}}while (begin1 <= end1){tmp[j++] = data[begin1++];}while (begin2 <= end2){tmp[j++] = data[begin2++];}memcpy(data + i, tmp + i, sizeof(int) * (end2 - i + 1));}gap *= 2;}free(tmp);
}

第二种解决方法:

直接对所有区间进行修正,将越界的区间修正成左区间大于右区间的不存在区间,此时不存在的区间将不会进入循环,而存在的区间也是有效区间,直接整体拷贝即可

void MergeSort_NotRecursion1(int* data, int sz)
{int* tmp = (int*)malloc(sizeof(int) * sz);assert(tmp);int gap = 1;while (gap < sz){for (int i = 0; i < sz; i += 2*gap){int begin1 = i;int end1 = i + gap - 1;int begin2 = i + gap;int end2 = i + 2 * gap - 1;int j = i;//1. end1 begin2 end2越界if (end1 >= sz){end1 = sz - 1;//修正的不存在区间begin2 = sz;end2 = sz - 1;}else if (begin2 >= sz)//2. begin2 end2 越界{//修正的不存在区间begin2 = sz;end2 = sz - 1;}else if(end2 >= sz)//3. end2越界{end2 = sz - 1;}while (begin1 <= end1 && begin2 <= end2){if (data[begin1] <= data[begin2])//当使用<=时防止出现相等时进行交换,使得排序稳定{tmp[j++] = data[begin1++];}else{tmp[j++] = data[begin2++];}}while (begin1 <= end1){tmp[j++] = data[begin1++];}while (begin2 <= end2){tmp[j++] = data[begin2++];}}memcpy(data, tmp, sizeof(int) * sz);gap *= 2;}free(tmp);
}

归并排序小优化

如果数据的个数特别大时,再让数据一直递归到只有一个数据的一层时会导致递归太深从而栈溢出,可以考虑在只有十个数据递归时采用其他排序算法进行优化,此处可以采用直接插入排序,因为每进行一次递归,数据会被分成两部分,所以当递归到只有十个数据时时,数据个数就已经比较小了

💡

这个优化只是在一定程度上有节省,当数据量特别大时,消耗和递归基本上一致

void InsertSort(int* data, int sz)
{for (int i = 1; i < sz; i++){int tmp = data[i];int end = i - 1;while (end > 0){if (data[end] > tmp){data[end + 1] = data[end];end--;}else{break;}}data[end + 1] = tmp;}
}//归并排序递归版本优化
void _MergeSort_modified(int* data, int* tmp, int left, int right)
{//确定递归结束条件if (left == right){return;}//小区间优化——直接插入排序if ((left - right + 1) < 10){InsertSort(data, left - right + 1);}//分割数组,首先确定当前数组的中间位置int mid = (left + right) / 2;_MergeSort_modified(data, tmp, left, mid);_MergeSort_modified(data, tmp, mid + 1, right);//取小的数值尾插到tmp数组中int begin1 = left;int end1 = mid;int begin2 = mid + 1;int end2 = right;int i = left;while (begin1 <= end1 && begin2 <= end2){if (data[begin1] < data[begin2]){tmp[i++] = data[begin1++];}else{tmp[i++] = data[begin2++];}}//存在一个数组先走完的情况while (begin1 <= end1){tmp[i++] = data[begin1++];}while (begin2 <= end2){tmp[i++] = data[begin2++];}//排序完之后将tmp数组中的数据拷贝回原来的数组memcpy(data + left, tmp + left, sizeof(int) * (right - left + 1));
}//归并排序递归版
void MergeSort_modified(int* data, int sz)
{//因为需要将排序好的数据重新拷贝到原来的数组中,所以需要开辟数组int* tmp = (int*)malloc(sizeof(int) * sz);assert(tmp);//防止主函数递归导致每次都会重新开辟空间,所以使用子函数_MergeSort_modified(data, tmp, 0, sz - 1);free(tmp);
}

归并排序的时间复杂度是,空间复杂度为,归并排序时稳定排序算法

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

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

相关文章

解决 :nvrtc: error: invalid value for --gpu-architecture (-arch)

核心&#xff1a;在显卡安装的cuda版本适配的pytorch中&#xff0c;更换pytorch的版本 刚遇到这个错误时&#xff0c;在网上搜索了一下&#xff0c;感谢博主1和博主2的解决方法带给我的启发。 标题服务器cuda是11.3版本&#xff0c;配置其他环境“御用”的pytorch安装语句 co…

社区维修平台|基于SpringBoot+ Mysql+Java+JSP技术的社区维修平台设计与实现(可运行源码+数据库+设计文档+部署说明+视频演示)

推荐阅读100套最新项目 最新ssmjava项目文档视频演示可运行源码分享 最新jspjava项目文档视频演示可运行源码分享 最新Spring Boot项目文档视频演示可运行源码分享 目录 前台功能效果图 住户后台功能 维修员前台功能 维修员后台功能 管理员功能登录 系统功能设计 数据库E…

用python实现Dubins曲线生成

Dubins曲线是连接两个具有指定方向和位置的点的最短路径&#xff0c;其中路径受到固定曲率约束&#xff08;如车辆的转向限制&#xff09;。Dubins曲线常用于机器人路径规划、车辆轨迹规划等领域。 Dubins曲线可以分为三种类型&#xff1a;CCC (Curve-Curve-Curve), CCL (Curv…

C++面试题和笔试题(四)

一、intx[6][4],(*p)[4];px;则*(p2)指向哪里&#xff1f; A X[0][1]B X[0][2]C X[1][0]D X[2][0] 官方解释&#xff1a; D int x[6][4], (*p)[4]; p x; 在这里&#xff0c;x 是一个二维数组&#xff0c;它有6行和4列。p 是一个指向具有4个整数的数组的指针。 当你执行…

DevOps-SonarQube整合Jenkins

下载SonarQube Scanner 登录Jenkins服务器&#xff0c;下载SonarQube Scanner wget https://binaries.sonarsource.com/Distribution/sonar-scanner-cli/sonar-scanner-cli-5.0.1.3006-linux.zip安装unzip&#xff0c;需要通过它来解压zip压缩包 yum install -y unzip解压So…

学习笔记——计算机网络(Internet、网络边缘)

一、Internet 网络是由多个计算机和其他网络设备通过通信链路相互连接而形成的互联网&#xff0c;用于实现数据传输和资源共享。它是现代信息社会中不可或缺的基础设施。 1.计算机网络&#xff1a; 通过通信链路连接&#xff1b; 以共享资源为目标&#xff1b; 资源包括&a…

基于Web的论文管理系统设计

目 录 目 录 III 摘 要 V 关键词 V Abstract VI Key Word VI 第一章 绪论 6 1.1系统设计背景 1 1.2系统设计目的与意义 1 1.3国内外现状 2 1.4本文结构 3 第二章 需求分析 3 2.1系统需求分析 4 2.2系统角色设计 4 第三章 系统开发技术 4 3.1 PHP语言简介和特点 5 3.2 Mysql数据…

【项目笔记】java微服务:黑马头条(day02)

文章目录 app端文章查看&#xff0c;静态化freemarker,分布式文件系统minIO1)文章列表加载1.1)需求分析1.2)表结构分析1.3)导入文章数据库1.3.1)导入数据库1.3.2)导入对应的实体类 1.4)实现思路1.5)接口定义1.6)功能实现1.6.1)&#xff1a;导入heima-leadnews-article微服务&am…

学C还是学C++?

计算机专业学生&#xff0c;大一上学期学习了C语言&#xff0c;下学期学校要学C&#xff0c;请问我寒假继续深入学习C还是提前学C,大佬们有什么建议吗&#xff1f;&#xff08;个人感觉C学的不是很明白&#xff0c;链表文件什么的还不是很懂…&#xff09; 这个并没有一个统一的…

流量池增长(6)

DMP和游戏化思维 流量的运营与挖掘 DMP: Data Management Platform&#xff0c;是一种用户数据标签化的管理和应用平台 DMP的价值 精准营销运营优化 建立DMP 用户标签化&#xff1a;与其他企业进行数据标签的撞库&#xff0c;进行精准的广告投放管理&#xff1a;执行营销策略…

7-3 截取字符串7-5 单词倒排

7-3 截取字符串&#xff08;分数 25&#xff09; 作者 刘琦 单位 天津城建大学 用户在三行中分别输入一个字符串s和两个整数m,n&#xff0c;输出字符串s中位于m和n&#xff08;包括m但不包括n&#xff0c;m<n&#xff09;之间的子字符串。 输入格式: 例如&#x…

数据库管理-第160期 Oracle Vector DB AI-11(20240312)

数据库管理160期 2024-03-12 数据库管理-第160期 Oracle Vector DB & AI-11&#xff08;20240312&#xff09;1 向量的函数操作to_vector()将vector转换为标准值vector_norm()vector_dimension_count()vector_dimension_format() 2 将向量转换为字符串或CLOBvector_seriali…

I O 流

IO流 啥是流 1、IO&#xff1a;输入 \ 输出流&#xff1a;一种抽象概念&#xff0c;是对数据传输的总称&#xff0c;也就是说&#xff0c;数据在设备间的传输称为流&#xff0c;流的本质&#xff0c;是数据传输IO流&#xff0c;就是用来&#xff0c;处理设备间的数据传输问题…

Android 使用adb操作WiFi相关指令

没有系统原生设置应用又需要调试WiFi功能时&#xff0c;可以使用如下指令来验证WiFi相关功能 最常用的就是 svc wifi enable/disable&#xff0c;再使用wpa_supplicant/wpa_cli来验证&#xff0c;但对于AP功能就没办法验证了&#xff0c;其实Android有组很强大的shell指令集&a…

明明jar包存在却报错找不到包名?两招教你解决java: 程序包org.springframework.context.annotation不存在问题!

一、问题提出 IDEA项目有时因为依赖库的问题出现出错&#xff1a; java: 程序包org.springframework.context.annotation不存在&#xff0c;如下图。 二、解决办法 方案1&#xff1a; 重新导入项目 ① 将项目中 .idea .iml 全部删除&#xff0c;项目重新导入 ② 用idea重新…

看完让你的RSA提升一个台阶 [GKCTF 2021]RRRRsa

阅读须知: 探索者安全团队技术文章仅供参考,未经授权请勿利用文章中的技术资料对任何计算机系统进行入侵操作,由于传播、利用本公众号所提供的技术和信息而造成的任何直接或者间接的后果及损失,均由使用者本人负责,作者不为此承担任何责任,如有侵权烦请告知,我们会立即删除…

2024计算机二级Python

1. 栈是先进先出&#xff0c;队是后进后出 2. 代码输出长度为5并不是\不占用位置&#xff0c;而是\与其后边的数字共同占用一个字符 3. 首先要弄清range函数此时表示的范围是前闭后开&#xff0c;不包含后面的数字&#xff0c;%函数表示的是余数&#xff0c;只有4是被整除的…

各种实用设置

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、命令行设置代理二、python包下载三、git设置代理 前言 一、命令行设置代理 export http_proxy"http://addr:port"二、python包下载 设置代理 …

sprintf函数和printf函数

在C语言中&#xff0c;sprintf函数和printf函数是两个非常常用的函数&#xff0c;它们都用于格式化输出字符串。尽管它们的名称很相似&#xff0c;但它们的功能却有所不同。 首先让我们来看一下printf函数。printf函数是C语言中最常用的输出函数之一&#xff0c;它用于将格式化…

批量处理数据:Java中的高效策略和实践

在处理大量数据时&#xff0c;尤其是在需要对数据库中的大批量记录进行更新时&#xff0c;有效的批处理策略是必不可少的。今天&#xff0c;我们将探讨一种在Java中批量更新数据的方法&#xff0c;并了解其在现实场景中的应用。 使用场景&#xff1a;重置视频标题 假设我们有…