理解归并排序的两种方法(超详细)

目录

前言

一.方法一:归并排序

1.1 归并思路

1.1.1 递归(分解)

1.1.2 区间(排序)

1.1.3 合并拷贝回原数组(合并)

二.归并排序过程

2.1 递归(分解)图解

2.2 归并有序区间(排序)图解

2.2.1 单独一趟排序

2.2.2 有序区间递归排序

 2.2.3 数组拷贝(合并)

2.3 归并全部代码

三.方法二:归并排序(非递归)

3.1 归并思路(非递归)

3.1.1 使用gap分割

3.1.2 比较合并

3.1.3 拷贝回原数组

3.2 归并过程(非递归)

3.2.1 多趟gap

3.2.2 合并过程

 3.3.3 归并(非递归)全部代码


前言

结合图文剖析归并排序的思路;学习分治法、递归思想、gap调整区间优化。

  1. 分治法(Divide and Conquer):归并排序是一种分治算法,它将问题分解成多个小问题,解决这些小问题,然后将它们的解决方案组合起来解决原始问题。

  2. 递归(Recursion):归并排序的实现通常依赖于递归,这是一种在函数内部调用自身的方法。递归是解决某些问题(如树的遍历、排序等)的强大工具。

  3. 稳定性(Stability):归并排序是一种稳定的排序算法,这意味着相等的元素在排序后保持它们原始的相对顺序。这对于某些应用场景非常重要。

  4. 时间复杂度(Time Complexity):归并排序的最坏、平均和最佳时间复杂度都是 O(nlog⁡n)O(nlogn),这使得它在大多数情况下都表现良好。

  5. 空间复杂度(Space Complexity):归并排序需要 O(n)O(n) 的额外空间来存储合并过程中的临时数组,这可能限制了它在空间受限的环境中的使用。

  6. 算法效率(Algorithm Efficiency):通过学习归并排序,您可以更好地理解不同排序算法的效率和适用场景。

  7. 代码实现:您将学会如何实现一个有效的归并排序算法,包括如何合并两个已排序的数组以及如何递归地分解和排序数组。

  8. 调试技巧(Debugging Skills):在实现归并排序的过程中,您可能会遇到数组越界、栈溢出等错误,学习如何解决这些问题可以提高您的调试技能。

  9. 内存管理(Memory Management):在非递归版本的归并排序中,您需要手动管理内存,包括分配和释放,这有助于您理解程序的内存使用情况。

  10. 优化(Optimization):了解如何优化归并排序,比如在小数组中使用插入排序以减少递归调用的开销。

  11. 算法适用性(Algorithm Applicability):知道何时使用归并排序是合适的,例如在大数据集排序或者需要稳定排序的场合。

  12. 并行计算(Parallel Computing):归并排序可以很容易地并行化,因为它的分治结构天然适合多线程或多处理器系统。

一.方法一:归并排序

1.1 归并思路

1.1.1 递归(分解)

1.先递归(分解),把问题分为子问题,

1.1.2 区间(排序)

2.把数组分割为不同区间(子问题)进行两区间排序

1.1.3 合并拷贝回原数组(合并)

把左右区间进行排序。注意:排序过程为:比较左右区间,排序好的先放入临时数组tmp中,再把数组tmp中的内容拷贝到原数组a中

二.归并排序过程

2.1 递归(分解)图解

//递归到最低层if (left >= right)return;//[left,mid][mid+1,right]有序,则可以合并,现在无序,子问题解决int mid = (left + right) / 2;MergeSort(a, left, mid, tmp);MergeSort(a, mid + 1, right, tmp);

递归过程如下:

如上图中的left mid ,当left>=right 时(left左区间大于或等于right右区间,也就是递归分解到个元素);此时我们分解已经为个元素了,接下来要做的事情是:归并[left,mid][mid+1,right]有序

2.2 归并有序区间(排序)图解

2.2.1 单独一趟排序
	//归并[left,mid][mid+1,right]有序int begin1 = left, end1 = mid;int begin2 = mid + 1, end2 = right;int index = left;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++];}

​编辑

注意:[left , mid] [mid+1 , right]
这两个区间有序时,才能进行归并。
        而递归分解到不可再分时。也就是只有一个数时 ,认为它是有序的。
如:左区间为数字7 9,右区间为数字3 6 单独一趟归并排序后左右区间排序合并,2个区间变为1个区间且该合并后的区间数组内容为有序的,内容为 3 6 7 9 。递归返回时,该区间再与新的有序区间进行比较,能一直保证左右区间为有序状态,即满足归并排序的要求。

2.2.2 有序区间递归排序

那么递归排序(前面是单趟排序,这里是多趟排序)中,子问题解决后,递归返回

 2.2.3 数组拷贝(合并)
	//把数组tmp中的数据拷贝到原数组a中for (int i = left; i <= right; i++){a[i] = tmp[i];}

这里合并拷贝完,接着就会返回上一层调用。也就是接着处理上一层、左右区间排序好的、数据更多的有序子问题

2.3 归并全部代码

归并排序,采用分治法,这里的归并排序通过(分解、排序、合并),不断解决子问题:进行排序也就是归并排序

void PrintfArr(int*a, int n)
{for (int i = 0; i < n; i++){printf("%d ", a[i]);}printf("\n");
}void MergeSort(int*a, int left, int right, int*tmp)
{//递归到最低层if (left >= right)return;//[left,mid][mid+1,right]有序,则可以合并,现在无序,子问题解决int mid = (left + right) / 2;MergeSort(a, left, mid, tmp);MergeSort(a, mid + 1, right, tmp);//归并[left,mid][mid+1,right]有序int begin1 = left, end1 = mid;int begin2 = mid + 1, end2 = right;int index = left;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++];}//把数组tmp中的数据拷贝到原数组a中for (int i = left; i <= right; i++){a[i] = tmp[i];}
}void TestMergeSort()
{int a[] = { 3,4,2,1,5,7,8,9,4,67,2,4,9,0,10 };PrintfArr(a, sizeof(a) / sizeof(a[0]));int* tmp = (int*)malloc(sizeof(int) * (sizeof(a) / sizeof(a[0])));MergeSort(a, 0, sizeof(a) / sizeof(a[0])-1, tmp);PrintfArr(a, sizeof(a) / sizeof(a[0]));}
int main()
{TestMergeSort();return 0;
}

三.方法二:归并排序(非递归)

3.1 归并思路(非递归)

3.1.1 使用gap分割

使用gap进行数组的分割处理,

3.1.2 比较合并

这里需要注意

int begin1 = i, end1 = i + gap - 1;
int begin2 = i + gap, end2 = i + 2 * gap - 1;
			//[i, i + gap -1][i + gap, i + 2*gap - 1]//修正区间 1.只有一组:if (begin2 >= n){break;}//修正区间 2.有两组:右边组没满if (end2 >= n){end2 = n - 1;}

情况一:只有左边一组(begin2到end2没数据)不需要合并,使用break跳过

这里就不对数组中最后的元素3进行合并了,等到后面gap扩大再处理。

情况二:含两组数据[i, i + gap -1][i + gap, i + 2*gap - 1]但是右边区间数据没放满,需要修正区间

3.1.3 拷贝回原数组

把tmp数组中的内容拷贝回原数组中

3.2 归并过程(非递归)

3.2.1 多趟gap

使用多趟gap,对数组需要排序的内容进行调整,这里的gap=gap*2;使用gap的2倍增长模拟归并排序,这里就没有使用递归的方法了。仅仅是数组内存的操作。

void MergeSortNonR(int* a, int n, int* tmp)
{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;//[i, i + gap -1][i + gap, i + 2*gap - 1]//修正区间 1.只有一组:if (begin2 >= n){break;}//修正区间 2.有两组:右边组没满if (end2 >= n){end2 = n - 1;}MergeArr(a, begin1, end1, begin2, end2, tmp);}gap *= 2;}
}

3.2.2 合并过程

通过左右区间进行比较,随着前面的gap的倍数增大,合并过程中的左右区间比较的范围也程倍数增长。2:2合成4    4:4合成8    8:8合成16.........若区间越界则需要处理。

MergeArr(int* a, int begin1, int end1, int begin2, int end2, int*tmp)
{int index = begin1;int begin = begin1, end = end2;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++];}//拷贝回原数组for (int i = begin; i <= end; i++){a[i] = tmp[i];}}

 3.3.3 归并(非递归)全部代码

归并排序(非递归全部代码)

void PrintfArry(int* a, int n)
{for (int i = 0; i < n; i++){printf("%d ", a[i]);}printf("\n");
}MergeArr(int* a, int begin1, int end1, int begin2, int end2, int*tmp)
{int index = begin1;int begin = begin1, end = end2;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++];}//拷贝回原数组for (int i = begin; i <= end; i++){a[i] = tmp[i];}}void MergeSortNonR(int* a, int n, int* tmp)
{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;//[i, i + gap -1][i + gap, i + 2*gap - 1]//修正区间 1.只有一组:if (begin2 >= n){break;}//修正区间 2.有两组:右边组没满if (end2 >= n){end2 = n - 1;}MergeArr(a, begin1, end1, begin2, end2, tmp);}gap *= 2;}
}void TestMergeSortNonR()
{int a[] = { 24,67,2,478,2,58,0,43,2,2,561,1,1,3,5,76 };int* tmp = malloc(sizeof(int) * sizeof(a) / sizeof(a[0]));PrintfArry(a, sizeof(a) / sizeof(a[0]));if (tmp == NULL)printf("malloc error");MergeSortNonR(a,sizeof(a)/sizeof(a[0]),tmp);PrintfArry(a, sizeof(a) / sizeof(a[0]));free(tmp);
}int main()
{TestMergeSortNonR();return 0;	
}

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

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

相关文章

SpringCloud(微服务介绍,远程调用RestTemplate,注册中心Nacos,负载均衡Ribbon,环境隔离,进程和线程的区别)【详解】

目录 一、微服务介绍 1. 系统架构的演变 1 单体架构 2 分布式服务 3 微服务 2. SpringCloud介绍 SpringCloud简介 SpringCloud版本 3. 小结 二、远程调用RestTemplate【理解】 1. 服务拆分 1 服务拆分原则 2 服务拆分示例 1) 创建父工程 2) 准备用户服务 1. 用户…

Vue 组件单元测试深度探索:细致解析与实战范例大全

Vue.js作为一款广受欢迎的前端框架&#xff0c;以其声明式的数据绑定、组件化开发和灵活的生态系统赢得了广大开发者的心。然而&#xff0c;随着项目规模的增长&#xff0c;确保组件的稳定性和可靠性变得愈发关键。单元测试作为软件质量的守护神&#xff0c;为Vue组件的开发过程…

Appium一本通

Appium介绍 概念&#xff1a;Appium是一个移动App(手机应用)自动化工具。 用途&#xff1a;重复性任务、爬虫、自动化测试。 特点&#xff1a;开源免费、多平台支持(ios\android)、多类型支持(native\webview)、类selenium支持多语言(java\python\js\ruby) Appium原理 三个主…

Java中的ArrayList

ArrayList<E>的特点 可调整大小的数组实现 <E>:是一种数据类型 ArrayList的构造方法 ArrayList list new ArrayList();创建一个空的集合对象 package dayhou40.day45; ​ import java.util.ArrayList; ​ public class Arraylisttest {public static void ma…

客户端连接ZK失败处理方案

文章目录 背景介绍报错信息处理方案第一步、查看zookeeper启动是否正常第二步、检查本地网络是否正常第三步、检查本地JDK版本 对于zookeeper服务注册中心&#xff0c;在前期【 Dubbo框架注册中心-Zookeeper搭建】博客中有环境搭建部署介绍&#xff0c;感兴趣可以参考安装。 背…

python使用opencv对图像的基本操作(2)

13.对多个像素点进行操作&#xff0c;使用数组切片方式访问 img[i,:] img[j,:] #将第j行的数值赋值给第i行 img[-2,:]或img[-2] #倒数第二行 img[:,-1] #最后一列 img[50:100,50:100] #50-100行&#xff0c;50-100列&#xff08;不包括第100行和第100列&#xff09; img[:100…

SpringCloud系列(18)--将服务提供者Provider注册进Consul

前言&#xff1a;在上一章节中我们把服务消费者Consumer注册进了Zookeeper&#xff0c;并且成功通过服务消费者Consumer调用了服务提供者Provider&#xff0c;而本章节则是关于如何将服务提供者Provider注册进Consul里 准备环境&#xff1a; 先安装Consul&#xff0c;如果没有…

ElasticSearch语句中must,must_not,should 组合关系

前言&#xff1a; 在实际应用中&#xff0c;发现当bool中同时使用must和should 没有达到想要的想过&#xff0c;而是只展示了must中的命中数据&#xff0c;所以打算探究一下bool中 三种逻辑关系的组合。 上述查询语句只展示了must的结果&#xff0c;没有should中的结果&#…

yolov8旋转目标检测输出的角度转化为适合机械爪抓取的角度

1. 机械爪抓取时旋转的角度定义 以X轴正方向&#xff08;右&#xff09;为零度方向&#xff0c;角度取值范围[-90&#xff0c;90)。 确认角度的方法&#xff1a; 逆时针旋转X轴&#xff0c;X轴碰到矩形框长边时旋转过的角度记为angleX&#xff1a; 1.如果angleX小于90&#xf…

RDD编程初级实践

参考链接 spark入门实战系列--8MLlib spark 实战_mob6454cc68310b的技术博客_51CTO博客https://blog.51cto.com/u_16099212/7454034 Spark和Hadoop的安装-CSDN博客https://blog.csdn.net/weixin_64066303/article/details/138021948?spm1001.2014.3001.5501 1. spark-shell…

【介绍下如何使用CocoaPods】

&#x1f3a5;博主&#xff1a;程序员不想YY啊 &#x1f4ab;CSDN优质创作者&#xff0c;CSDN实力新星&#xff0c;CSDN博客专家 &#x1f917;点赞&#x1f388;收藏⭐再看&#x1f4ab;养成习惯 ✨希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出…

Linux:服务器间同步文件的脚本(实用)

一、功能描述 比如有三台服务器&#xff0c;hadoop102、hadoop103、hadoop104&#xff0c;且都有atguigu账号 循环复制文件到所有节点的相同目录下&#xff0c;且脚本可以在任何路径下使用 二、脚本实现 1、查看环境变量 echo $PATH2、进入/home/atguigu/bin目录 在该目录下…

Redis 源码学习记录:字符串

redisObject Redis 中的数据对象 server/redisObject.h 是 Redis 对内部存储的数据定义的抽象类型其定义如下&#xff1a; typedef struct redisObject {unsigned type:4; // 数据类型&#xff0c;字符串&#xff0c;哈希表&#xff0c;列表等等unsigned encoding:4; …

网页提示语闪太快的定位问题(selenium)

selenium UI自动化时&#xff0c;提示语闪太快&#xff0c;导致无法获取元素的问题 解决办法 步骤一&#xff1a; F12---》控制台输入debugger 步骤二&#xff1a;对于需要定位的部分&#xff0c;在控制台的debugger处回车&#xff0c;可以定住页面 步骤三&#xff1a;正常定…

【CTF Web】CTFShow web14 Writeup(PHP+switch case 穿透+SQL注入+文件读取)

web14 5 解法 <?php include("secret.php");if(isset($_GET[c])){$c intval($_GET[c]);sleep($c);switch ($c) {case 1:echo $url;break;case 2:echo A;break;case 555555:echo $url;case 44444:echo "A";break;case 3333:echo $url;break;case 222…

win11 安装qt5.14.2 、qtcreator、vs编译器 。用最小安装进行 c++开发qt界面

系统 &#xff1a;win11 一、安装vs生成工具 &#xff0c;安装编译器 下载visualstudio tools 生成工具&#xff1a; 安装编译器 和 windows sdk&#xff1a; 安装debug 调试器&#xff1a; 二、Qt5.14.2下载 下载链接: Index of /archive/qt/5.14/5.14.2 安装qt 三、配置QT/…

ChuanhuChatGPT集成百川大模型

搭建步骤&#xff1a; 拷贝本地模型&#xff0c;把下载好的Baichuan2-7B-Chat拷贝到models目录下 修改modules\models\base_model.py文件&#xff0c;class ModelType增加Baichuan Baichuan 16 elif "baichuan" in model_name_lower: model_type ModelType.Ba…

短视频矩阵营销系统 poihuoqu 任意文件读取漏洞复现

0x01 产品简介 短视频矩阵营销系统是由北京华益云数据科技有限公司开发的一款产品,这家公司专注于抖音短视频矩阵营销系统的研发,致力于为企业提供全方位的短视频营销解决方案。华益云抖销短视频矩阵系统可以帮助企业快速搭建多个短视频账号,实现内容的批量制作和发布,提高…

Vue从0-1学会如何自定义封装v-指令

文章目录 介绍使用1. 理解指令2. 创建自定义指令3. 注册指令4. 使用自定义指令5. 自定义指令的钩子函数6. 传递参数和修饰符7. 总结 介绍 自定义封装 v-指令是 Vue.js 中非常强大的功能之一&#xff0c;它可以让我们扩展 Vue.js 的模板语法&#xff0c;为 HTML 元素添加自定义行…

Java毕业设计 基于SpringBoot vue城镇保障性住房管理系统

Java毕业设计 基于SpringBoot vue城镇保障性住房管理系统 SpringBoot 城镇保障性住房管理系统 功能介绍 首页 图片轮播 房源信息 房源详情 申请房源 公示信息 公示详情 登录注册 个人中心 留言反馈 后台管理 登录 个人中心 修改密码 个人信息 用户管理 房屋类型 房源信息管理…