C语言归并排序

以梦为马,不负韶华

在这里插入图片描述

文章目录

      • 引入:
      • 实现原理
      • 问题引出:
      • 递归实现:
      • 迭代实现
      • 稳定性分析:
      • 总结:

引入:

如何将两个有序数组(假设为升序)合并为一个有序数组?

双指针法,如果第一个数组的第一个元素大于第二个数组的元素,就取第二个(即较小的那个放在合并的数组的首位置),然后在比较第一个数组第一个元素与第二个数组的第二个元素,以此类推,终将有一个数组的元素先被访问完,剩下的那个数组的元素一定是大于已经排序后的数组,直接将未排完序的数组的元素添加到我们要合并数组即可。
在这里插入图片描述

代码如下

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++];}

实现原理

 归并排序就是利用上边的思想进行排序,将无序的数组不断折中至最短,并在合并的过程中将两个数组进行合并并排序。其是建立在归并操作上的一种有效的排序方式,采用分治法,将已经排好序的子序列合并,得到完全有序的序列,在归的过程中将待排序的数组分为各个小块,在并的过程中不断使每个小区间变为有序,最终使该数组有序。
过程刨析
在这里插入图片描述

问题引出:

能否在原数组进行排序?

  • 如果直接进行数据覆盖的话明显不可以,会将未修改的元素覆盖,直接修改了数组元素,这种想法必然是错误的。
  • 聪明的同学会想到那么我用交换的形式呢?

看下边这种情况
在这里插入图片描述

 第二个小数组区间已经排好的序已经被打乱,在后边的排序中,比较时就是先和5比,再和4比,这就违反了我们两个有序数组分别从第一个元素比大小将小的那个放在合并的数组的第一个位置的核心思想。

 引出空间复杂度,顺便也说一说他的时间复杂度,我想大家已经有所猜测了,毕竟和二分法沾边的算法。
空间复杂度
 使用归并排序的方法时要开一份同样大小的数组装载这个新数组,比较时用原数组,放置时用开好的新数组。因为递归时空间可以复用,所以归并排序的空间复杂度明显是O(N)。
时间复杂度
 时间复杂度是容易判断,由递归的深度和判断的次数即可得出归并排序的时间复杂度,二分法将递归深度变为log(N),数据量为N,故时间复杂度为O(N*logN)。

递归实现:

void MergeSort(int* a, int n)
{int* tmp = (int*)malloc(sizeof(int) * n);if (tmp == NULL){perroe("malloc fail");return;}_MergeSort(a, tmp, 0, n - 1);free(tmp);
}
void _MergeSort(int* a, int* tmp, int begin, int end)
{if (end <= begin)return;int mid = (end + begin) / 2;MergeSort(a, tmp, begin, mid);MergeSort(a, tmp, mid + 1, end);//归并到tmp数组,再拷贝回去//a->[begin,mid][mid+1,end]->tmp拷贝过去,新开空间int begin1 = begin, end1 = mid;int begin2 = mid + 1, end2 = end;int index = begin;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++];}memcpy(a + begin, tmp + begin, (end - begin + 1) * sizeof(int));
}

 要注意递归的结束条件已经想要用递归来做什么,不断分化小区间,然后调用小区间的归并,直至每个数组只有一个元素,然后再用两个有序数组合并为一个有序数组的方法,将两个数组合并。

 上图展开中好似前半段和后半段的排序是同时进行的,其实不然,根据代码就可以看出,递归先排序好数组的前半部分,再排序数组的后半部分
在这里插入图片描述

 在分割时是有顺序的,如果递归的过程还是不够清晰可以自己尝试画一画递归展开图,也可移步二叉树问题详谈递归对递归的过程获得更大的理解和掌握。


迭代实现

无疑是借助==循环(迭代)==来实现。
 仔细想一想我们会发现,在递归的过程中我们需要的是不断获得分割后每一小区间数组的坐标,分割直至每一个小数组只有一个元素为止,然后一个数组有两个元素,然后继续合并为八个元素,直至数组的每个坐标的值都被访问一遍为止。

 如果大家已经看了快速排序的非递归版,可能会想到用栈来实现,然而这里用栈是不能解决这里的问题的,快速排序可以用栈是因为数组个数end在使用之后就pop出栈也没有影响,然而归并排序还需要知道划分的范围最后要合并数组,然而如果想要实现递归的过程,前边的数据都已经被pop掉,无法确定数组范围,就无法借此来合并。

如动图所示:
在这里插入图片描述
当然,如果一定要用栈或队列来实现他,会很麻烦,因为有更好的方法可以解决。
要跳出递归的思路,利用循环的方法,归并的思想。
如图所示
在这里插入图片描述
首先一个一个元素排序合并,两个两个元素进行排序并合并,两两排序后,每两个元素就变成有序的了,然后四个四个合并并排序,然后八个和八个,这样就将数组所由元素利用归并的方式进行排序。

void MergeSortNonR(int* a, int n)
{int* tmp = (int*)malloc(sizeof(int) * n);if (tmp == NULL){perror("malloc fail");return;}int gap = 1;while (gap < n){for (int i = 0; i < n; i += 2 * gap){int begin1 = i, end1 = i + gap - 1;int begin2 = i + gap, end2 = i + 2 * gap - 1;//[begin1,end1]  [begin2,end2]归并//如果第二组不存在,这一组就不用归并啦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++];}memcpy(a + i, tmp + i, (end2 - i + 1) * sizeof(int));}printf("\n");gap *= 2;}free(tmp);
}

下边是上述代码的解释:

 可以看到,起始条件下gap的值为1,第一次循环时,将begin1为0,end1为0,begin2为1,end2为1。
判断大小,将小的放前边,大的放后边,最后归并到原数组

memcpy(a + i, tmp + i, (end2 - i + 1) * sizeof(int));

 由于i=0。拷贝回原数组时,a+i还是a的位置,拷贝数量在-0+1后变为了2,就将判断后的两个元素归并到原数组,且已经排好了序。

  • 然后下次循环,i+2,指向了数组第三个元素。
    begin1=2,end1=2,begin2=3,end2=3。
    这时就是第二个元素第三个元素排序归并,直至走完这一次for循环,gap*=2,回到外层while循环,gap<n继续走,此时gap为2,再次进入for循环时,每次跳跃的间隔就是2,就实现了上边所说,将数组依次分为一个和一个排序归并,两个两个排序归并,然后4个和4个归并。。。。。。

注意:这里while结束的条件为gap>n,在循环内,一定会出现这样的情况,虽然gap的值没有大于n,但在for循环中,i的值加等于二倍的gap,所以begin2一定会大于n,这就会造成越界访问,这时就不应该继续执行循环,直接break出去,然后在while循环结尾,gap会*=2,当gap的值大于n时,就代表数组已经排序完了。

//如果第二组不存在,这一组就不用归并啦if (begin2 >= n){break;}

 还有一种情况就是begin2没有越界,然而end2越界,这时第二个数组还是存在的,直接将end2更改为n-1即可。

if (end2 >= n)
{end2 = n - 1;
}

稳定性分析:

 归并排序是一个稳定的排序,除了空间复杂度比较大,其他的都是优点,通过上边的演示,可以发现,在归并的过程中相同数据的位置排列不会发生变化。

总结:

 归并排序是一种很不错的排序方式,如果追求高效率且需要稳定性,归并排序是首当其冲的最优解,对于当前的计算机来说花费一点内存不算什么,归并排序的思想很简单,困难的是细节的把握,相对于其他相同时间复杂度的其他排序方式,大都是不稳定的,所以总体而言归并排序在排序界还是有一席之地的。

今天的内容就到这里,欢迎大家留言反馈。
在这里插入图片描述

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

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

相关文章

yolov5/v7修改标签和检测框显示【最全】

《记录自己在使用yolov5遇到的一些问题》同时也供大家参考&#xff0c;如果对你们有帮助&#xff0c;希望大家可以给个点赞、收藏鼓励下&#xff0c;非常感谢&#xff01; 以自带的一张图片作为示例,yolov5(6.1版本)的初始检测框应该是如下图所示 修改线条粗细、隐藏标签、隐…

EI论文故障识别程序:DBN深度置信/信念网络的故障识别Matlab程序,数据由Excel导入,直接运行!

​适用平台&#xff1a;Matlab2021b版及以上 本程序参考中文EI期刊《基于变分模态分解和改进灰狼算法优化深度置信网络的自动转换开关故障识别》中的深度置信网络&#xff08;Deep Belief Network&#xff0c;DBN&#xff09;部分进行故障识别&#xff0c;程序注释清晰&#x…

Python之学生信息管理系统

目录 一、基础界面实现 1、主函数 2、保持循环&#xff0c;获取用户需求 二、函数实现模块功能 1、添加学生信息 2、删除学生信息 3、修改学生信息 4、查找全部学生信息 5、退出系统 三、整合代码 1、 完整代码 2、完整实现过程 实现 打印功能菜单、添加学生信息、删…

想自学软件测试?一般人我还是劝你算了吧。。。

&#x1f4e2;专注于分享软件测试干货内容&#xff0c;欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1f4dd; 如有错误敬请指正&#xff01;&#x1f4e2;交流讨论&#xff1a;欢迎加入我们一起学习&#xff01;&#x1f4e2;资源分享&#xff1a;耗时200小时精选的「软件测试」资…

<keep-alive>作用及用法

<keep-alive>是Vue.js的内置组件。它用于缓存具有相同组件树的组件。当组件使用<keep-alive>包裹时&#xff0c;组件不会被销毁&#xff0c;而是会缓存到内存中&#xff0c;等到下次再次渲染时&#xff0c;直接使用缓存中的组件实例。 <keep-alive>有以下几…

【Linux】共享内存

文章目录 一、共享内存的原理详谈共享内存的实现过程二、共享内存的接口函数1.shmget2. shmatshmdtshmctl 进程间使用共享内存通信三、共享内存的特性 关于代码 一、共享内存的原理 共享内存是由操作系统维护和管理的一块内存。 共享内存的本质是内核级的缓冲区。 一个进程向…

C语言精华题目锦集1

第一题 test.c文件中包括如下语句&#xff0c;文件中定义的四个变量中&#xff0c;是指针类型的是&#xff08;&#xff09;【多选】 #define INT_PTR int* typedef int* intptr; INT_PRT a,b; int_ptr c,d;A:a  B:b  C:c  D:d #define是宏定义&#xff0c;此时在程序中IN…

SQLite3 数据库学习(六):Qt 嵌入式 Web 服务器详解

参考引用 SQLite 权威指南&#xff08;第二版&#xff09;SQLite3 入门 1. Apache 搭建 cgi 环境 1.1 什么是 Apache Apache 是世界使用排名第一的 Web 服务器软件 它可以运行在几乎所有广泛使用的计算机平台上&#xff0c;由于其跨平台和安全性被广泛使用 1.2 具体搭建流程…

一、用户管理

一、后端数据库初始化 1.1 因为版本问题&#xff0c;始终报错&#xff0c;按照报错信息去查询解决方案&#xff0c;无法解决 灵机一动&#xff1a; 网址&#xff1a; Spring Boot 3.0 升级 实战踩坑记录 - 掘金 (juejin.cn) &#xff11;.&#xff12; 个人配置【运行成功…

c++的三目运算符

C三目运算符增强 C中的三目运算符表达式返回的可以是一个变量&#xff0c;但是C语言中返回的是一个常量。 C语言中&#xff1a; void test05() { int a 10; int b 20; printf("%d\n", a < b ? a : b); //在C语言中三目运算符返回的是表达式的值&am…

Javascript每天一道算法题(十三)——最大子数组和_中等

文章目录 动态规划题三个重要步骤&#xff08;了解思路&#xff09;1、问题2、示例3、解决方法&#xff08;1&#xff09;方法1——动态规划 总结 动态规划题三个重要步骤&#xff08;了解思路&#xff09; &#xff08;1&#xff09;定义数组元素的含义 用一个数组来保存历史数…

2020年06月 Scratch(三级)真题解析#中国电子学会#全国青少年软件编程等级考试

Scratch等级考试(1~4级)全部真题・点这里 一、单选题(共25题,每题2分,共50分) 第1题 执行以下脚本后舞台上的角色将 ? A:先克隆自身,克隆体出现后被删除。 B:先克隆自身,克隆体出现后删除本体。 C:克隆出自身后本体与克隆体同时被删除。 D:克隆出自身后本体与克…

docker常用命令, 镜像版本的导入、导出并加载,打包镜像的命令

文章目录 docker常用命令&#xff1a;打镜像包&#xff1a;镜像版本的导入、导出并加载 docker常用命令&#xff1a; 打镜像包&#xff1a; ​ docker build -t calc:20230630 /home/apps/calc/docker/ 删除某个镜像的版本&#xff0c;allen_mysql的5.7版本 docker rmi all…

Redis深入理解-内核请求处理流程、数据传输协议

Redis 内核级请求处理流程 Redis Server 其实就是 Linux 服务器中的一个进程 主要还是下图的流程 应用先和 server 端建立 TCP 连接建立连接之后&#xff0c;server 端就会有一个与该客户端通信的 socket&#xff0c;客户端的读写请求发送到服务端的 socket那么通过 IO 多路…

分组背包问题学习笔记 AcWing 9. 分组背包问题

原题 有 N&#xfffd; 组物品和一个容量是 V&#xfffd; 的背包。 每组物品有若干个&#xff0c;同一组内的物品最多只能选一个。 每件物品的体积是 vij&#xfffd;&#xfffd;&#xfffd;&#xff0c;价值是 wij&#xfffd;&#xfffd;&#xfffd;&#xff0c;其中 …

PC8233(CC/CV控制)高耐压输入5V/3.4A同步降压电路内建补偿带恒流恒压输出

概述 PC8233&#xff08;替代CX8853&#xff09;是一款同步降压调节器,输出电流高达3.4A,操作范围从8V到32V的宽电源电压。内部补偿要求最低数量现成的标准外部组件。PC8233在CC&#xff08;恒定输出电流&#xff09;模式或CV&#xff08;恒定输出电压&#xff09;模式&#x…

【前端】前端监控⊆埋点

文章目录 前端监控分为三个方面前端监控流程异常监控常见的错误捕获方法主要是 try / catch 、window.onerror 和window.addEventListener 等。Promise 错误Vue 错误React 错误 性能监控用户行为监控常见的埋点方案来源 前端监控分为三个方面 异常监控&#xff08;监控前端页面…

基于element-ui后台模板,日常唠嗑

后面会补充github地址 文章目录 目录 文章目录 案例说明 1.引入库 2.创建布局组件 3.创建布局组件 4.菜单效果展示 5.创建顶部组件 5.创建顶部面包屑组件 6.创建内容区域组件 7.效果总览 7.布丁&#xff08;实现一些小细节&#xff09; 前言一、pandas是什么&#xff1f;二、使…

CentOS7中升级OpenSSL详细教程

文章目录 一. 引言二. 升级前的准备1.备份现有配置2. 检查系统版本3. 安装依赖 三. OpenSSL安装四. 验证 一. 引言 OpenSSL: 是用于保护数据安全的重要工具。它能提供加密&#xff0c;解密等多项功能。 然而&#xff0c;随着技术的发展和新的安全漏洞的出现&#xff0c;使用最…

管理类联考——英语二——备考 100 句涵盖所有词汇

全中 在海里的这个地区&#xff0c;熊猫们喜欢就着苏打碗豆喝茶。而大洋州的民兵则喜欢经过半岛&#xff0c;带着编剧本的公式上餐厅去。附件的电影院里有额外的歌剧和香蕉&#xff0c;这一时代的斑马们被外面的天线所吸引。实验室里的蟹想用它的肋骨去戳四肢象灯炮的小羊。但…