常见排序算法之归并排序

目录

一、什么是归并排序

二、递归实现

2.1 思路

2.2 C语言源码

三、非递归实现

3.1 思路

3.2 C语言源码


一、什么是归并排序

归并排序是一种基于分治思想的排序算法。它的基本思想是将原始的待排序序列不断地分割成更小的子序列,直到每个子序列中只有一个元素,然后逐步将这些子序列合并为一个有序的序列。

具体来说,归并排序的操作流程如下:

  1. 分割阶段:将待排序序列不断地二分为更小的子序列,直到每个子序列只包含一个元素。
  2. 合并阶段:将相邻的子序列两两合并,合并过程中按照大小顺序排列元素,直到所有的子序列合并为一个有序序列。

归并排序由于是自上而下的递归排序,每次合并操作都需要额外的空间来存储临时的序列,因此归并排序的空间复杂度较高。但由于合并操作是稳定的,因此归并排序是一种稳定的排序算法。

归并排序的时间复杂度为 O(nlogn),其中 n 为待排序序列的长度,因此归并排序在时间上具有较高的效率。由于其稳定性和高效性,归并排序通常被用于需要稳定性的排序场景,或者对于大数据量的排序。

可以看出,归并排序类似于快速排序的思想,都是基于分治法的二叉树思路实现。但是二者存在区别:

  • 快速排序是一种类似前序的思路-先排序大区间再排序小区间
  • 归并排序是一种类似后序的思路-先排子区间再排大区间最后合并

这样的区别会体现在代码实现中递归代码的位置上。

二、递归实现

2.1 思路

核心思路:将每个小区间数组在新数组上排序,然后复制回原数组上。重复递归,由小区间到大区间,直至排序完成

  • 考虑单次
  1. 比较逻辑:两个区间分别初始化两个指针指向起始位置,逐个元素遍历比较,按照大小顺序放入新数组中。
  2. 收尾逻辑:区间内或许存在顺序已经排好的序列,比较逻辑结束后将他们放入新数组中
  3. 最后将新数组拷贝到原数组中,一个区间排序完成
  • 考虑递归
  1. 每次计算区间的中点进行二分法
  2. 传入[起始位置,中点] 以及 [中点的下一个位置,结束位置] 进行重复递归
  3. 递归结束的条件是,子区间仅有一个元素(意味着不需要排列)

注意事项:

实际写代码的过程中,每次开辟空间的做法大大降低了程序执行的效率。更好的处理方法是:开辟于要排序数组等大的空间,每次小区间复制只需传入区间的起始位置,区间的大小就可以避免空间的重复开辟。

2.2 C语言源码

//递归版本
void _MergeSort(int* a, int begin, int end, int* tmp)
{
	if (begin == end)
	{
		return;
	}
	int mid = (begin + end) / 2;
	//递归
	_MergeSort(a, begin, mid, tmp);
	_MergeSort(a, mid + 1, end, tmp);

	//归并
	int begin1 = begin;
	int end1 = mid;
	int begin2 = mid + 1;
	int end2 = end;

	int i = begin;
	while (begin1 <= end1 && begin2 <= end2)
	{
		if (a[begin1] < a[begin2])
		{
			tmp[i++] = a[begin1++];
		}
		else
		{
			tmp[i++] = a[begin2++];
		}
		
	}
	//收尾
	while (begin1 <= end1)
	{
		tmp[i++] = a[begin1++];
	}
	while (begin2 <= end2)
	{
		tmp[i++] = a[begin2++];
	}
	//复制到指定位置
	memcpy(a + begin, tmp + begin, sizeof(int) * (end - begin + 1));
}

void MergeSort(int* a, int n)
{
	int* tmp = (int*)malloc(sizeof(int) * n);
	if (tmp == NULL)
	{
		perror("malloc");
		exit(1);
	}
	_MergeSort(a, 0, n - 1, tmp);
	free(tmp);
	tmp = NULL;
}

三、非递归实现

3.1 思路

与快速排序的非递归实现思路类似,本质上仍然是要控制每次传入的区间。但是前文有讲二者的细微差别,所以实现方式不尽相同。本文采用二路循环方式的方式实现。

采用自底向上的思路讲解

比较的主题逻辑依然是采用上述递归思路的逻辑,所以仍然需要两个指针指向两个组,gap变量表示每组处理的数据。

  • 考虑一次处理:
    gap=1,两路,处理的是两个数据。排序后将中间数组复制到原数组的指定位置
  • 考虑一层处理:因为二路处理一次只能有限个数据,需要再来一次循环处理掉该层的所有数据。结束条件很明显要小于数组的大小,每次循环起始位置都是原位置+2*gap
  • 考虑循环处理:一层结束后,下一层gap要变成原来的2倍。很明显循环结束条件是gap小于数组大小。

注意事项:

上述图解的元素个数恰好是偶数,若出现奇数个的情况,在求区间时一定会发生数组越界的情况。需要找到特殊情况然后进行修正

  1. 由于begin1指针由数组大小循环来控制,所以不可能造成越界。
  2. 考虑end1越界:那么后续的两个指针同样越界,此时区间内只有一个元素无需排序,跳出循环即可
  3. 考虑begin2越界:此时区间内有两个元素,在上一层排序中二者已经为有序,所以跳出循环即可。
  4. 考虑end2越界:此时第二个组中仅有一个元素,依然需要比较排序。所以需要将end2的下标进行修正。

3.2 C语言源码

//非递归版本
void MergeSortNonR(int* a, int n)
{
	int* tmp = (int*)malloc(sizeof(int) * n);
	if (tmp == NULL)
	{
		perror("malloc");
		exit(1);
	}
	
	int gap = 1;	// 每组归并的数据个数

	while (gap < n)
	{
		for (int j = 0; j < n; j += 2 * gap)
		{
			int begin1 = j, end1 = begin1 + gap - 1;
			int begin2 = end1 + 1, end2 = begin2 + gap - 1;

			//特殊情况控制
			if (end1 >= n || begin2 >= n)
			{
				break;
			}
			if (end2 >= n)
			{
				end2 = n - 1;
			}
			//开始排序
			int i = j;
			while (begin1 <= end1 && begin2 <= end2)
			{
				if (a[begin1] < a[begin2])
				{
					tmp[i++] = a[begin1++];
				}
				else
				{
					tmp[i++] = a[begin2++];
				}
			}
			//收尾
			while (begin1 <= end1)
			{
				tmp[i++] = a[begin1++];
			}
			while (begin2 <= end2)
			{
				tmp[i++] = a[begin2++];
			}
			//每组结束复制
			memcpy(a + j, tmp + j, sizeof(int) * (end2 - j + 1));
		}
		//循环
		gap *= 2;
	}
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

白酒:不同产地白酒的口感差异与品鉴技巧

云仓酒庄豪迈白酒作为中国白酒的品牌之一&#xff0c;其口感和品质深受消费者喜爱。然而&#xff0c;不同产地的白酒在口感上存在一定的差异&#xff0c;了解这些差异以及掌握正确的品鉴技巧&#xff0c;对于更好地品味云仓酒庄豪迈白酒以及其他不同产地的白酒至关重要。 首先&…

计网期末复习指南(四):网络层(IP协议、IPv4、IPv6、CIDR、ARP、ICMP)

前言&#xff1a;本系列文章旨在通过TCP/IP协议簇自下而上的梳理大致的知识点&#xff0c;从计算机网络体系结构出发到应用层&#xff0c;每一个协议层通过一篇文章进行总结&#xff0c;本系列正在持续更新中... 计网期末复习指南&#xff08;一&#xff09;&#xff1a;计算…

使用新的 NVIDIA Isaac Foundation 模型和工作流程创建、设计和部署机器人应用程序

使用新的 NVIDIA Isaac Foundation 模型和工作流程创建、设计和部署机器人应用程序 机器人技术的应用正在智能制造设施、商业厨房、医院、仓库物流和农业领域等各种环境中迅速扩展。该行业正在转向智能自动化&#xff0c;这需要增强机器人功能&#xff0c;以执行感知、绘图、导…

【人工智能】第四部分:ChatGPT的技术实现

人不走空 &#x1f308;个人主页&#xff1a;人不走空 &#x1f496;系列专栏&#xff1a;算法专题 ⏰诗词歌赋&#xff1a;斯是陋室&#xff0c;惟吾德馨 目录 &#x1f308;个人主页&#xff1a;人不走空 &#x1f496;系列专栏&#xff1a;算法专题 ⏰诗词歌…

【WEB前端2024】3D智体编程:乔布斯3D纪念馆-第35课-3D互动教材

【WEB前端2024】3D智体编程&#xff1a;乔布斯3D纪念馆-第35课-3D互动教材 使用dtns.network德塔世界&#xff08;开源的智体世界引擎&#xff09;&#xff0c;策划和设计《乔布斯超大型的开源3D纪念馆》的系列教程。dtns.network是一款主要由JavaScript编写的智体世界引擎&am…

关于科技的总结与思考

文章目录 互联网时代有趣的数字数据驱动大数据的两个特性数据保护互联网免费模式的再探讨平台互联网的意义人工智能伦理的思考语言理性人梅特卡夫定律冲浪的神奇之处AR的恐怖之处叙词表、受控词表和大众分类法六度/十九度的解读知识图谱是真正的仿生智能幂次法则和优先连接现代…

怎么把图片压缩小一点?让你的图片秒变小清新!

怎么把图片压缩小一点&#xff1f;在数字化时代&#xff0c;图片已经成为我们生活中不可或缺的一部分。无论是社交媒体的分享&#xff0c;还是工作文档的编辑&#xff0c;图片都扮演着重要的角色。然而&#xff0c;随着图片数量的增加&#xff0c;存储空间的问题也日益凸显。幸…

AI烟火识别算法在消防安全与火灾预警系统中的应用与价值

在信息化和智能化的今天&#xff0c;烟火识别算法作为一种重要的技术工具&#xff0c;在火灾预防和处理中发挥着关键作用。其工作原理主要基于深度学习和图像处理技术&#xff0c;能够实时分析监控画面&#xff0c;准确检测出图像中的烟火&#xff0c;并发出预警。 一、烟火识…

优思学院|为什么精益生产总是搞不成功?CLMP

先说一个故事 有一位老板希望模仿乔布斯&#xff0c;怎么模仿呢&#xff1f; 他穿起黑色高领毛衣&#xff0c;李维斯蓝色牛仔裤和New Balance运动鞋。 不过&#xff0c;企业之后也没有和苹果一样好&#xff0c;老板们觉得很奇怪啊&#xff0c;是不是哪里有问题&#xff0c;乔…

vscode专区

1.展示多行的文件导航标签,而非只有1行 1.1打开设置 1.2搜索该设置"workbench.editor.wrap.tabs",并勾选 1.3效果对比

MySQL(四) - SQL优化

一、SQL执行流程 MySQL是客户端-服务器的模式。一条SQL的执行流程如下&#xff1a; 在执行过程中&#xff0c;主要有三类角色&#xff1a;客户端、服务器、存储引擎。 大致可以分为三层&#xff1a; 第一层&#xff1a;客户端连接到服务器&#xff0c;构造SQL并发送给服务器…

vue3 实现自定义指令封装 --- 通俗易懂

1、局部自定义指令 1.1 在<script setup>定义组件内的指令&#xff0c;任何以v开头的驼峰式命名的变量都可以被用作一个自定义指令 <template><div><h3>使用自定义指令</h3><div>########################## start 局部自定义指令</d…

MFC实现子控件focus焦点上下移动父控件ListView和Gridview也跟着向上下移动

项目中要实现mfc功能&#xff0c;然后子控件焦点下移&#xff0c;LIstView和Gridview父控件不会下移&#xff0c;所以就有这个文章。废话不多说直接上代码。 MFCGridView.java import android.content.Context; import android.util.AttributeSet; import android.view.View;…

TiKV学习5:TiDB SQL执行流程

目录 1. DML语句读流程概要 2. DML语句写流程概要 3. DDL 流程概要 4. SQL的Parse和Compile 5. 读取的执行 6. 写入的执行 7. DDL的执行 8. 小结 1. DML语句读流程概要 TiDB Server接收sql并处理&#xff0c;TiKV负责持久化数据&#xff0c;PD提供TSO和Region的数据字典…

error /var/lib/jenkins/workspace/*/node_modules/node-sass: Command failed.

原因&#xff1a;node-sass版本不一致 版本图&#xff1a; 解决方案&#xff1a; 进入到jenkins项目目录下&#xff0c;修改package.json文件 将7.0.1改成6.0.1版本

《对马岛之魂:导演剪辑版》新鲜出炉,AOC电竞显示器与你并肩作战!

超越PS版本的画面表现&#xff0c;AOC U27G3XM助你轻松拉满游戏体验&#xff01; 近日&#xff0c;《对马岛之魂&#xff1a;导演剪辑版》正式登陆PC平台。这款备受期待的作品不仅在战斗机制和故事内容上进行了创新&#xff0c;还引入了更高级的图形选项和更丰富的自定义设置。…

性能测试(二)—— linux服务器监控性能测试

测试目的&#xff1a;发现服务器的性能瓶颈。配置的不同能够承载的最大任务数不同&#xff0c;能够承载的压力也不同 服务器性能测试范围 1.1 测试范围 CPU 内存 硬盘 网络 版本&#xff08;软件、应用版本&#xff09; 1.2 测试与生产配置不相同 多次性能压测预估 …

力扣----轮转数组

题目链接&#xff1a;189. 轮转数组 - 力扣&#xff08;LeetCode&#xff09; 思路一 我们可以在进行每次轮转的时候&#xff0c;先将数组的最后一个数据的值存储起来&#xff0c;接着将数组中前n-1个数据依次向后移&#xff0c;最后将存储起来的值赋给数组中的第一个数据。 …

运动模糊技术在AI绘画中的创新应用

引言&#xff1a; 随着人工智能技术的飞速发展&#xff0c;AI绘画已经成为艺术创作领域的一颗新星。它不仅改变了艺术家的创作方式&#xff0c;还为普通用户提供了前所未有的艺术体验。在众多AI绘画技术中&#xff0c;运动模糊技术以其独特的视觉效果和广泛的应用前景受到了广泛…

【排序】插入排序,希尔排序

前面我们讲述了冒泡排序和选择排序&#xff0c;我们本章讲的排序方法是插入排序&#xff0c;插入排序是希尔排序实现的基础函数&#xff0c;大家一定要好好理解插入排序的逻辑&#xff0c;这样才能在后面学习希尔排序的时候&#xff0c;更容易的去理解&#xff0c;我们直接开始…