这个 归并排序详解过程 我能吹一辈子!!!

news2025/4/4 22:05:15

文章目录

  • 归并排序概念
  • 归并排序算法思路
    • 归并排序递归实现
    • 归并排序非递归实现

归并排序概念

1945年,约翰·冯·诺依曼(John von Neumann)发明了归并排序,这是典型的分治算法的应用。

归并排序(Merge sort)是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。

归并排序算法思路

归并排序算法有两个基本的操作,一个是分,也就是把原数组划分成两个子数组的过程。另一个是治,它将两个有序数组合并成一个更大的有序数组。

1> 将待排序的线性表不断地切分成若干个子表,直到每个子表只包含一个元素,这时,可以认为只包含一个元素的子表是有序表。
2> 将子表两两合并,每合并一次,就会产生一个新的且更长的有序表,重复这一步骤,直到最后只剩下一个子表,这个子表就是排好序的线性表。
在这里插入图片描述

相信大家都知道如何将两个有序序列合为一个有序序列吧:
在这里插入图片描述

那么如何得到有序的子序列呢?当序列分解到只有一个元素或是没有元素时,就可以认为是有序了,这时分解就结束了,开始合并:
在这里插入图片描述

归并排序递归实现

归并排序,从其思想上看就很适合使用递归来实现,并且用递归实现也比较简单。其间我们需要申请一个与待排序列大小相同的数组用于合并过程两个有序的子序列,合并完毕后再将数据拷贝回原数组。

代码示例:

//归并排序(子函数)
void _MergeSort(int* a, int left, int right, int* tmp)
{
	if (left >= right)//归并结束条件:当只有一个数据或是序列不存在时,不需要再分解
	{
		return;
	}
	int mid = left + (right - left) / 2;//中间下标
	_MergeSort(a, left, mid, tmp);//对左序列进行归并
	_MergeSort(a, mid + 1, right, tmp);//对右序列进行归并
	int begin1 = left, end1 = mid;
	int begin2 = mid + 1, end2 = right;
	//将两段子区间进行归并,归并结果放在tmp中
	int i = left;
	while (begin1 <= end1&&begin2 <= end2)
	{
		//将较小的数据优先放入tmp
		if (a[begin1] < a[begin2])
			tmp[i++] = a[begin1++];
		else
			tmp[i++] = a[begin2++];
	}
	//当遍历完其中一个区间,将另一个区间剩余的数据直接放到tmp的后面
	while (begin1 <= end1)
		tmp[i++] = a[begin1++];
	while (begin2 <= end2)
		tmp[i++] = a[begin2++];
	//归并完后,拷贝回原数组
	int j = 0;
	for (j = left; j <= right; j++)
		a[j] = tmp[j];
}
//归并排序(主体函数)
void MergeSort(int* a, int n)
{
	int* tmp = (int*)malloc(sizeof(int)*n);//申请一个与原数组大小相同的空间
	if (tmp == NULL)
	{
		printf("malloc fail\n");
		exit(-1);
	}
	_MergeSort(a, 0, n - 1, tmp);//归并排序
	free(tmp);//释放空间
}

时间复杂度:O(N*logN)  空间复杂度:O(N)

归并排序非递归实现

归并排序的非递归算法并不需要借助栈来完成,我们只需要控制每次参与合并的元素个数即可,最终便能使序列变为有序:
在这里插入图片描述
当然,以上例子是一个待排序列长度比较特殊的例子,我们若是想写出一个广泛适用的程序,必定需要考虑到某些极端情况:

情况一
 当最后一个小组进行合并时,第二个小区间存在,但是该区间元素个数不够gap个,这时我们需要在合并序列时,对第二个小区间的边界进行控制。
在这里插入图片描述

情况二
 当最后一个小组进行合并时,第二个小区间不存在,此时便不需要对该小组进行合并。
在这里插入图片描述

情况三
 当最后一个小组进行合并时,第二个小区间不存在,并且第一个小区间的元素个数不够gap个,此时也不需要对该小组进行合并。(可与情况二归为一类)
在这里插入图片描述

只要把控好这三种特殊情况,写出归并排序的非递归算法便轻而易举了。

代码示例:

//归并排序(子函数)
void _MergeSortNonR(int* a, int* tmp, int begin1, int end1, int begin2, int end2)
{
	int j = begin1;
	//将两段子区间进行归并,归并结果放在tmp中
	int i = begin1;
	while (begin1 <= end1&&begin2 <= end2)
	{
		//将较小的数据优先放入tmp
		if (a[begin1] < a[begin2])
			tmp[i++] = a[begin1++];
		else
			tmp[i++] = a[begin2++];
	}
	//当遍历完其中一个区间,将另一个区间剩余的数据直接放到tmp的后面
	while (begin1 <= end1)
		tmp[i++] = a[begin1++];
	while (begin2 <= end2)
		tmp[i++] = a[begin2++];
	//归并完后,拷贝回原数组
	for (; j <= end2; j++)
		a[j] = tmp[j];
}
//归并排序(主体函数)
void MergeSortNonR(int* a, int n)
{
	int* tmp = (int*)malloc(sizeof(int)*n);//申请一个与待排序列大小相同的空间,用于辅助合并序列
	if (tmp == NULL)
	{
		printf("malloc fail\n");
		exit(-1);
	}
	int gap = 1;//需合并的子序列中元素的个数
	while (gap < n)
	{
		int i = 0;
		for (i = 0; i < n; i += 2 * gap)
		{
			int begin1 = i, end1 = i + gap - 1;
			int begin2 = i + gap, end2 = i + 2 * gap - 1;
			if (begin2 >= n)//最后一组的第二个小区间不存在或是第一个小区间不够gap个,此时不需要对该小组进行合并
				break;
			if (end2 >= n)//最后一组的第二个小区间不够gap个,则第二个小区间的后界变为数组的后界
				end2 = n - 1;
			_MergeSortNonR(a, tmp, begin1, end1, begin2, end2);//合并两个有序序列
		}
		gap = 2 * gap;//下一趟需合并的子序列中元素的个数翻倍
	}
	free(tmp);//释放空间
}

时间复杂度:O(N*logN)  空间复杂度:O ( N )

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

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

相关文章

【2023秋招】华为od22-12-14三道题(思路+代码)

2023大厂笔试模拟练习网站&#xff08;含题解&#xff09; www.codefun2000.com 最近我们一直在将收集到的各种大厂笔试的解题思路还原成题目并制作数据&#xff0c;挂载到我们的OJ上&#xff0c;供大家学习交流&#xff0c;体会笔试难度。现已录入200道互联网大厂模拟练习题&…

Godot引擎 4.0 文档 - 循序渐进教程 - 监听玩家输入

本文为Google Translate英译中结果&#xff0c;DrGraph在此基础上加了一些校正。英文原版页面&#xff1a; Listening to player input — Godot Engine (stable) documentation in English 监听玩家输入 在上一课创建您的第一个脚本的基础上&#xff0c;让我们看看任何游戏…

Linux基本指令实现4及热键指令详解

目录 Linux热键补充&#xff1a; 1.bc指令&#xff1a; Tab键的智能补充&#xff1a; ctrlc键&#xff1a; uname指令&#xff1a; lscpu指令&#xff1a; lsmem指令&#xff1a; df -h指令&#xff1a; 关机指令&#xff1a; 扩展指令&#xff1a; Linux热键补充&#…

【MySQL通关之旅】从山脚到山顶(傲视群雄版)

查 基本查询 select 查询指定 name,age 字段并返回 select name,age from 表名 查询所有字段并返回 select * from 表名 查询指定 name,age 字段并起别名(姓名,性别) 返回 select name as ‘姓名’,age as ‘性别’ from 表名 去除某字段重复记录 select distinct 字段名 from…

RSA数学基础 连分数

前言 最近在学rsa,补一下连分数 定义 形如 a 0 1 a 1 1 a 2 1 a 3 1 … a_{0}\frac{1}{a_{1}\frac{1}{a_{2}\frac{1}{a_{3}\frac{1}{\ldots}}}} a0​a1​a2​a3​…1​1​1​1​ 的分数 连分数分为有限连分数无限连分数 如上是一个无限连分数 任何一个有理数都可以表示…

【youcans 的图像处理学习课】21. Haar 小波变换

专栏地址&#xff1a;『youcans 的图像处理学习课』 文章目录&#xff1a;『youcans 的图像处理学习课 - 总目录』 【youcans 的图像处理学习课】21. Haar 小波变换 1. 小波变换1.1 小波变换基本概念例程 17_1&#xff1a;常用小波族的图像 1.2 连续小波变换1.3 离散小波变换&a…

【C生万物】 字符串内存函数篇 (下)

欢迎来到 Claffic 的博客 &#x1f49e;&#x1f49e;&#x1f49e; &#x1f449; 专栏&#xff1a;《C生万物 | 先来学C》&#x1f448; 前言&#xff1a; 承接上篇&#xff0c;在认识了常用的字符串和内存函数后&#xff0c;带大家实现几个函…

idea把项目上传到码云

1. 为项目创建仓库 2. 选中中项目右击git, 先add, 在commit Directory 3. 设置远程码云项目地址 4. push项目, ok。 注意&#xff1a; 如果你在最后push出现以下提示&#xff0c;则说明提交失败 Push to origin/master was rejected(译文&#xff1a;推送到原点/master被拒绝…

联合阿里在职测开工程师耗时一个星期写的 【接口测试+自动化接口接口测试详解]

前言&#xff1a; 兄弟们如果本文让你感觉能学到东西就麻烦各位动动发财的手来个一键三连&#xff0c;听说点赞&#xff0c;收藏&#xff0c;评论&#xff0c;关注的兄弟已经跳槽涨薪发财了哦。 【文章末尾给大家留下了大量的福利】 1&#xff1a;json模块的使用  字典是一种…

华为OD机试真题 Java 实现【最大花费金额】【2023Q1】

一、题目描述 双十一众多商品进行打折销售&#xff0c;小明想购买自己心仪的一些物品&#xff0c;但由于受购买资金限制&#xff0c;所以他决定从众多心仪商品中购买三件&#xff0c;而且想尽可能的花完资金。 现在请你设计一个程序帮助小明计算尽可能花费的最大资金数额。 …

MMDetection 3D入门

MMDetection 3D入门 文章目录 MMDetection 3D入门介绍架构模块核心模块与相关组件公共基础模块 依赖安装最佳实践验证安装 测试数据create_data.py修改数据目录 train.py整体流程命令行参数配置文件中为什么传入参数是 dictRunner函数中参数含义结合图示来理清其中数据的流向与…

深度学习图像识别模型:递归神经网络

深度学习是一种人工智能技术&#xff0c;它用于解决各种问题&#xff0c;包括自然语言处理、计算机视觉等。递归神经网络&#xff08;Recurrent Neural Network&#xff0c;RNN&#xff09;是深度学习中的一种神经网络模型&#xff0c;主要用于处理序列数据&#xff0c;例如文本…

电子电气架构——IP地址获取方式汇总

我是穿拖鞋的汉子,魔都中坚持长期主义的工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 我们并不必要为了和谐,而时刻保持通情达理;我们需要具备的是,偶尔有肚量欣然承认在某些方面我们可能会有些不可理喻。该有主见的时候能掷地有声地镇得住场…

基于MATLAB的数字信号处理第1章

1.正弦振动程序 %Sample1_1 dt 0.02; %采样间隔 f3; %采样频率为3Hz t0:dt:5; %持续时间 x0.5*sin(2*pi*f*t1); %信号 plot(t,x); %绘制信号图形 xlabel(时间/s); %显示横坐标信息 ylabel(振幅); %显示纵坐标信息 2.两同频振动的合成 %Sample1_2; f3; dt…

博学谷学习记录】超强总结,用心分享 | 架构师 JVM内核调优学习总结

文章目录 jvm整体架构1.java运行过程2.jvm模型 运行数据区1.程序计数器1.1 概述1.2 溢出异常1.3 案例 2.虚拟机栈2.1 概述2.2 溢出异常 3.本地方法栈3.1 概述3.2 溢出异常 4.堆4.1 概述4.2 jdk1.74.3 jdk1.84.4 溢出异常 5.方法区5.1 概述5.2 溢出异常5.3 案例&#xff1a;1.6/…

数学动点问题1

文章目录 讲解&#xff08;1&#xff09;&#xff08;2&#xff09;&#xff08;3&#xff09;&#xff08;4&#xff09; 讲解 &#xff08;1&#xff09; 将点 C ( 3 , 1 ) 代入直线 y − x b &#xff0c;得 将点\ C(3,1)\ 代入直线\ y-xb\ &#xff0c;得 将点 C(3,1)…

C++三大特性—多态 “虚函数与动态绑定”

面向对象程序设计的核心思想是数据抽象、继承、动态绑定。 通过使用数据对象&#xff0c;将类的接口与实现分离 使用继承&#xff0c;定义相似的类型并对其相似关系建模 使用动态绑定&#xff0c;可以在一定程度上忽略相似类型的区别&#xff0c;而以统一的方式使用它们的对象 …

Linux Audio (6) DAPM-3 damp的kcontrol注册过程

DAPM-3 damp的kcontrol注册过程 普通kcontrolDAMP kcontrol第一步 codec驱动add widget第二步 Mechine驱动add kcontrol damp的注册过程 普通kcontrol 定义&#xff1a; static const struct snd_kcontrol_new wm8960_snd_controls[] { SOC_DOUBLE_R_TLV("Capture Volu…

创建 ROS 的消息和服务(四)

执行命令 cd ~/catkin_ws/src/catkin_create_pkg beginner_tutorials std_msgs roscpp rospy进入刚刚那个功能包begineer什么的 cd beginner_tutorials/ mkdir msgecho "int64 num" > msg/num.msg 然后添加如下代码,按i 然后输入 <build_depend>message_…

C++:EffectiveC++:Article21:必须返回对象时,别妄想返回其Reference

Article21&#xff1a;必须返回对象时&#xff0c;别妄想返回其Reference 1. operator* 以by value 方式返回一个结果2. operator* 以 by Reference 方式返回一个结果3 定义static Rational 对象总结 本章主要介绍&#xff1a;函数返回值两种类型&#xff1a;值类型返回和引用返…