排序算法——归并排序以及非递归实现

一、归并排序思想

归并排序(MERGE-SORT)是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(Divide andConquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。 归并排序核心步骤 :

我们现在要树立这么一个思路,分解数组并不是真的分解数组,而是可以通过下标的形式来区分数组。归并排序实际上就是将一个数组分解到不能子啊分解的部分,两两经行比较排序,再将数据更新到新的数组中去。这非常类似于我们学的数的后序遍历。

二、代码实现

1、单趟:

我们先写单趟,因为归并排序是对半分组,所以最后为两个区间经行比较,思路转化为之前我们学过的两个数组的合并

int mid = (rigt + left) / 2;
int begin1 = left;
int end1 = mid-1;
int begin2 = mid;
int end2 = rigt;
int i = left;
while (begin1 <= end1 && begin2 <= end2)
{

	if (a[begin1] > a[begin2])
	{
		tmp[i++] = a[begin2++];
	}
	else
	{
		tmp[i++] = a[begin1++];
	}
}
while (begin1<=end1)
{
	tmp[i++] = a[begin1++];
}
while (begin2 <= end2)
{
	tmp[i++] = a[begin2++];
}
memcpy(a+left, tmp+left, sizeof(int) * (rigt - left + 1));

这里要注意两个问题:1、区间怎么划分合适,2、为什么每排序一次就得将数组拷贝回去

 2、单趟问题解释

首先我们先解决第一个问题:区间怎么划分合适?

我们这里以下面图为例:

因为有-1的存在很容易导致下标为负数,最后导致数组的违法访问。同时可能还会有死循环的问题:

具体原因如下:

但是如果改为区间为 

int begin1 = left;
int end1 = mid;
int begin2 = mid+1;
int end2 = rigt;就不会出现这个问题:

所以正确代码如下:

int mid = (rigt + left) / 2;
int begin1 = left;
int end1 = mid;
int begin2 = mid+1;
int end2 = rigt;
int i = left;
while (begin1 <= end1 && begin2 <= end2)
{

	if (a[begin1] > a[begin2])
	{
		tmp[i++] = a[begin2++];
	}
	else
	{
		tmp[i++] = a[begin1++];
	}
}
while (begin1<=end1)
{
	tmp[i++] = a[begin1++];
}
while (begin2 <= end2)
{
	tmp[i++] = a[begin2++];
}
memcpy(a+left, tmp+left, sizeof(int) * (rigt - left + 1));

第二个问题:因为比较的时候是在原数组经行比较如果不及时将数组内容更新,可能会导致再排序时经行错误的大小比较,最后导致结果错误 

3、总体代码实现

结束条件当分割区间等于1或者小于1的时候。

void _MergeSort(int* a, int* tmp, int left, int rigt)
{
	if (left >= rigt)
	{
		return;
	}
	int mid = (rigt + left) / 2;
	_MergeSort(a, tmp, left, mid);
	_MergeSort(a, tmp, mid + 1, rigt);
	int begin1 = left;
	int end1 = mid;
	int begin2 = mid+1;
	int end2 = rigt;
	int i = left;
	while (begin1 <= end1 && begin2 <= end2)
	{

		if (a[begin1] > a[begin2])
		{
			tmp[i++] = a[begin2++];
		}
		else
		{
			tmp[i++] = a[begin1++];
		}
	}
	while (begin1<=end1)
	{
		tmp[i++] = a[begin1++];
	}
	while (begin2 <= end2)
	{
		tmp[i++] = a[begin2++];
	}
	memcpy(a+left, tmp+left, sizeof(int) * (rigt - left + 1));
}
void MergeSort(int* a, int n)
{
	int* tmp = (int*)malloc(sizeof(int) * n);
	if (tmp == NULL)
	{
		return;
	}
	else
	{
		_MergeSort(a, tmp, 0, n - 1);
		free(tmp);
		tmp = NULL;
	}
}

三、非递归

对于归并排序我们实现非递归用循环更好一点,非递归我们可以直接从递归回去那出发,定义一个组个数,通过控制组的个数实现“并”这个过程:

还是我们先写一个数组只有一个数的情况:

for (int i = 0; i < end - begin + 1; i += 2 * gap)//每次比较两组,所以加2gap
{
	int begin1 = i;
	int end1 = begin1 + gap - 1;
	int begin2 = begin1 + 2 * gap - 1;//第二组的开头与第一组的开头差2倍
	int end2 = begin2 + gap - 1;
	int k = i;
	while (begin1 <= end1 && begin2 <= end2)
	{

		if (a[begin1] > a[begin2])
		{
			tmp[k++] = a[begin2++];
		}
		else
		{
			tmp[k++] = a[begin1++];
		}
	}
	while (begin1 <= end1)
	{
		tmp[k++] = a[begin1++];
	}
	while (begin2 <= end2)
	{
		tmp[k++] = a[begin2++];
	}
	memcpy(a + i, tmp + i, sizeof(int) * (2 * gap));//同理这里也得排序一次就拷贝回去

}

那么后面就是gap*2控制即可:

void _MergeSortNonR(int* a, int* tmp,int begin, int end)
{
	int gap = 1;
	for (int j = gap; j < end-begin+1; j *= 2)
	{
		for (int i = 0; i < end - begin + 1; i += 2 * gap)//每次比较两组,所以加2gap
		{
			int begin1 = i;
			int end1 = begin1 + gap - 1;
			int begin2 = begin1 + 2 * gap - 1;//第二组的开头与第一组的开头差2倍
			int end2 = begin2 + gap - 1;
			int k = i;
			while (begin1 <= end1 && begin2 <= end2)
			{

				if (a[begin1] > a[begin2])
				{
					tmp[k++] = a[begin2++];
				}
				else
				{
					tmp[k++] = a[begin1++];
				}
			}
			while (begin1 <= end1)
			{
				tmp[k++] = a[begin1++];
			}
			while (begin2 <= end2)
			{
				tmp[k++] = a[begin2++];
			}
			memcpy(a + i, tmp + i, sizeof(int) * (2 * gap));
		}
	}
}

但是上面的代码仍然存在问题,和快排类似这里会存在数组越界问题:这里我们打印数组下标区间来观察一下:、

大致我们可以分为这两种情况:

 如果end1越界,那么说明分为了只有这一组数据,不用排了直接跳出循环,如果end2越界我们就需要给end2修改值。

所以改进代码如下:

void _MergeSortNonR(int* a, int* tmp,int begin, int end)
{
	int gap = 1;
	for (int j = gap; j < end-begin+1; j *= 2)
	{
		for (int i = 0; i < end - begin + 1; i += 2 * gap)//每次比较两组,所以加2gap
		{
			int begin1 = i;
			int end1 = begin1 + gap - 1;
			int begin2 = begin1 + 2 * gap - 1;//第二组的开头与第一组的开头差2倍
			int end2 = begin2 + gap - 1;
			int k = i;
			// 第二组都越界不存在,这一组就不需要归并
			if (begin2 >= end - begin + 1)
				break;

			// 第二的组begin2没越界,end2越界了,需要修正一下,继续归并
			if (end2 >= end - begin + 1)
				end2 = end - begin + 1 - 1;
			while (begin1 <= end1 && begin2 <= end2)
			{

				if (a[begin1] > a[begin2])
				{
					tmp[k++] = a[begin2++];
				}
				else
				{
					tmp[k++] = a[begin1++];
				}
			}
			while (begin1 <= end1)
			{
				tmp[k++] = a[begin1++];
			}
			while (begin2 <= end2)
			{
				tmp[k++] = a[begin2++];
			}
			memcpy(a + i, tmp + i, sizeof(int) * (2 * gap));
		}
	}
}
void MergeSortNonR(int* a, int n)
{
	int* tmp = (int*)malloc(sizeof(int) * n);
	if (tmp == NULL)
	{
		return;
	}
	else
	{
		_MergeSortNonR(a, tmp, 0, n - 1);
		free(tmp);
		tmp = NULL;
	}
}

可能有人会想如果只剩一组数据了,我直接return行不行?实际上是可以的,因为break跳出循环后在gap*2还是只有一组数据,所以直接return即可。

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

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

相关文章

使用Python绘制瀑布图

使用Python绘制瀑布图 瀑布图效果代码 瀑布图 瀑布图&#xff08;Waterfall Chart&#xff09;是一种数据可视化工具&#xff0c;用于展示累积数值的变化&#xff0c;尤其适合于展示随时间或过程中的增减变化。它通常用于财务分析&#xff0c;如展示收入、支出和净利润的变化过…

分离式光电液位传感器与浮球开关相比具有哪些优势

分离式光电液位传感器与浮球开关相比有哪些优势&#xff1f;分离式光电液位传感器依据光学原理&#xff0c;在传统光学传感器的基础上进行了改进。其特点是将光学组件分离出来&#xff0c;置于水箱外部感应&#xff0c;而传感器本身则独立于水箱外。这种设计有效解决了浮球开关…

CPU内部结构窥探·「1」

CPU内部逻辑运算单元是如何运行的 引言 中央处理器&#xff08;CPU&#xff09;是计算机的大脑&#xff0c;负责处理各种计算任务。在CPU里面&#xff0c;有一个重要的部分叫做逻辑运算单元&#xff08;ALU&#xff0c;Arithmetic Logic Unit&#xff09;。ALU就像一个超级计…

JS面试题:hash和history的区别

一、hash 模式和 history 模式的介绍 由于 Vue 项目为单页面应用&#xff0c;所以整个项目在开发和构建过程中&#xff0c;仅存在一个HTML物理文件。通过路由系统可以实现将项目的组件与可访问的URL路径进行绑定。由于Vue项目只有一个HTML物理文件&#xff0c;切换页面时既需要…

vivado BD_INTF_NET、BD_INTF_PIN

BD_INTF_NET 描述 接口是一组信号&#xff0c;它们共享一个共同的功能&#xff0c;同时包含 单个信号和多条总线。例如&#xff0c;AXI4Lite主机包含一个 单个信号的数量加上多条总线&#xff0c;这些都是制作 联系通过将这些信号和总线分组到一个接口中&#xff0c;Vivado IP积…

VisualStudio中:如果某个项目不显示SVN的show log等,而其他项目都正常

VisualStudio中&#xff1a;如果某个项目不显示SVN的show log等&#xff0c;而其他项目都正常。说明大概率是当前项目的问题&#xff0c;而不是VisualStudio的问题&#xff01; 1.这个项目内有一个“隐藏”文件夹.svn 》先删除&#xff01; 2.如果外层文件夹有红色感叹号&…

英伟达剧透新一代最强 GPU;奥特曼公开回应 AI 语音争议丨 RTE 开发者日报 Vol.217

开发者朋友们大家好&#xff1a; 这里是 「RTE 开发者日报」&#xff0c;每天和大家一起看新闻、聊八卦。我们的社区编辑团队会整理分享 RTE&#xff08;Real-Time Engagement&#xff09; 领域内「有话题的新闻」、「有态度的观点」、「有意思的数据」、「有思考的文章」、「…

重学java 59.Properties属性集集合嵌套集合下总结

不要咀嚼小小悲观&#xff0c;而忘掉整个世界 —— 24.6.3 一、Properties集合&#xff08;属性集&#xff09; 1.概述 Properties 继承 于HashTable 2.特点 a、key唯一&#xff0c;value可重复 b、无序 c、无索引 d、线程安全 e、不能存null键&#xff0c;null值 f、Propertie…

idea项目maven下载依赖报错

报错&#xff1a; 1、Failure to find bad.robot:simple-excel:jar:1.0 in https://maven.aliyun.com/repository/public was cached in the local repository, resolution will not be reattempted until the update interval of aliyunmaven has elapsed or updates are forc…

小程序集arcgis地图显示自定义坐标的功能实现记录!(学习笔记)

最近再做一个新能源回收项目&#xff0c;项目中有个根据回收点坐标数据显示区域内回收点位置&#xff0c;点击图标直接导航到该位置&#xff0c;及分布的需求&#xff0c;研究了一下&#xff0c;实现效果如下&#xff0c;实现起来很简单&#xff0c;代码及效果 回收点位置及分…

Linux - 逻辑卷的创建和管理

1.逻辑卷LVM的创建 1.1 创建步骤 ①添加硬盘或者创建分区 ②创建物理卷 pvcreate ③创建卷组 vgcreate ④创建逻辑卷 lvcreate ⑤创建文件系统 mkfs.xfs/ect4/... ⑥创建挂…

随身wifi哪个牌子的最好用?网速最快的随身wifi推荐测评,随身wifi罗永浩推荐!

现在很多人都开始使用随身WiFi&#xff0c;因为互联网发达&#xff0c;看视频、刷抖音、看直播等等都需要流量&#xff0c;手机流量不够用&#xff0c;流量需求也很高。因此随身WiFi逐渐出现在人们的视野中&#xff0c;在众多品牌中一款名为格行的随身wifi被各明星和千万网红争…

Docker基础篇之本地镜像发布到阿里云

文章目录 1. 本地镜像发布到阿里云的流程2. 阿里云开发平台3. 将自己的本地镜像推送到阿里云 1. 本地镜像发布到阿里云的流程 阿里云ECS Docker生态如下图所示&#xff1a; 2. 阿里云开发平台 在控制台找到容器和镜像服务&#xff1a; 然后创建一个个人实例&#xff1a; 下面…

HW面试常见知识点2——研判分析(蓝队中级版)

&#x1f340;文章简介&#xff1a;又到了一年一度的HW时刻&#xff0c;本文写给新手想快速进阶HW蓝中的网安爱好者们&#xff0c; 通读熟练掌握本文面试定个蓝中还是没问题的&#xff01;大家也要灵活随机应变&#xff0c;不要太刻板的回答&#xff09; &#x1f341;个人主页…

江大白 | 万字长文,AIGC算法工程师的面试秘籍,推荐收藏!

本文来源公众号“江大白”&#xff0c;仅用于学术分享&#xff0c;侵权删&#xff0c;干货满满。 原文链接&#xff1a;万字长文&#xff0c;AIGC算法工程师的面试秘籍&#xff0c;推荐收藏&#xff01; 以下文章来源于微信公众号&#xff1a;WeThinkln 作者&#xff1a;Roc…

模块概述

自学python如何成为大佬(目录):https://blog.csdn.net/weixin_67859959/article/details/139049996?spm1001.2014.3001.5501 模块的英文是Modules&#xff0c;可以认为是一盒&#xff08;箱&#xff09;主题积木&#xff0c;通过它可以拼出某一主题的东西。这与第6章介绍的函…

《互联网政务应用安全管理规定》电子邮件安全如何整改?

继上篇文章&#xff08;解读《互联网政务应用安全管理规定》网络和数据安全中的身份认证和审计合规&#xff09;之后&#xff0c;本篇文章继续解读第五章“电子邮件安全”&#xff0c;为党政机关事业单位提供电子邮件系统整改思路。 “电子邮件安全”内容从第三十一条到第三十…

机器学习的热门领域及应用趋势

机器学习的热门领域及应用趋势 近年来&#xff0c;机器学习&#xff08;Machine Learning, ML&#xff09;已经成为科技领域的热门话题&#xff0c;其在各个行业的应用越来越广泛和深入。本文将详细介绍当前机器学习的几个热门领域&#xff0c;以及人们在这些领域中使用的机器…

【数据结构】图论——Prim算法和Kruskal算法

目录 Prim算法和Kruskal算法Prim算法的原理数据结构算法步骤解释算法实现代码示例 Kruskal 算法Kruskal算法的原理和步骤Kruskal算法的实现数据结构并查集操作Kruskal算法 Prim算法和Kruskal算法 文章: 【数据结构】图论&#xff08;图的储存方式&#xff0c;图的遍历算法DFS和…

Ai绘画工具Stable Diffusion,手把手教你训练你的专属Lora模型,神级教程建议收藏!

哈喽&#xff0c;大家好&#xff0c;我是设计师阿威。 今天给大家带来的是Stable Diffusion训练Lora的教程&#xff0c;希望对大家有帮助。 一、硬件要求 我们知道Stable Diffusion WebUI对显卡要求比较高&#xff0c;同样Lora训练对显卡要求更高&#xff0c;所以要想训练一…