归并排序含非递归版

这篇具有很好参考价值的文章主要介绍了归并排序含非递归版。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

目录

1.归并排序的原理

 2.实现归并排序

2.1框架

2.2区间问题和后序遍历

2.3归并并拷贝

2.4归并排序代码

2.5测试

3.非递归实现归并排序 

3.1初次实现

3.2测试 

3.3修改

 3.4修改测试


1.归并排序的原理

归并排序是建立在归并操作上的一种有效的排序算法,该算法是采用分治法的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;

即先使每个子序列有序,再使子序列段间有序   若将两个有序表合并成一个有序表,称为二路归并。 

可以将数组内容想象成一颗二叉树,通过递归的方式将数组的内容分为左子树和右子树,分出来的左子树和右子树又分别有它们的左子树和右子树。不断地向下进行拆分,直到拆分到没有对应区间和区间只有一个元素(有序)的时候便递归返回。然后使用归并的方式将左子树和右子树归并成有序数组,再将其拷贝会原数组,这样对应的子树便会有序,再递归返回,不断地递归。直到根左子树和根右子树有序,再归并和拷贝,整个数组就会变得有序。

如图所示:归并排序含非递归版,数据结构与算法,算法

 2.实现归并排序

归并排序需要开额外的空间来辅助实现   之所以不在原数组实现,是因为如果你使用交换的方式来进行排序,可能会将原数组已经排好序的部分又一次变回无序,而使用令数组向后移动的方式进行对应的排序,时间复杂度便会大大增加,因此归并排序可以理解成一种以空间换时间的排序方法。

归并排序需要开额外的空间,但每次递归都要开空间显然是不合理的,因此我们使用一个函数来完成递归的部分。思考一下,新创建的函数的参数应该有哪些,首先得有原数组,其次得有我们开辟好的数组,而我们要如二叉树一般形成对应的递归,显然需要区间,而区间的形成需要两个数来辅助,因此可以传递两个代表区间的数进来,可以取名为begin,end(left,right),这样理解起来轻松一些。

2.1框架

归并排序含非递归版,数据结构与算法,算法

2.2区间问题和后序遍历

如二叉树一般实现后序遍历注意: 当begin>=end时代表着区间不存在或者只有一个元素(有序)的时候返回。因为是后序遍历,需要先将区间的分界给计算出来之后才能使用。

归并排序含非递归版,数据结构与算法,算法

2.3归并并拷贝

可以看出,每次递归都会有两个区间的生成[begin,mid]和[mid+1,end]我们的目标就是将这两个区间归并在一起,这个很简单,循环便可以搞定。注意:两个区间不知道是哪个区间先完成循环,因此在外面需要将未完成循环的区间,补充回辅助数组中。

搞定完归并后,使用memcpy将辅助数组中的内容拷贝回原数组,排序便结束了。注意:我们传递的是闭区间,因此拷贝的长度为,end-begin+1

归并排序含非递归版,数据结构与算法,算法

2.4归并排序代码

void MergeSort(int*arr,int n)
//将数组和数组的元素个数传递过来
{
	int* a = (int*)malloc(sizeof(int)*n);
	//创建辅助数组
	if (a == NULL)
	{
		perror("malloc fail");
		return;
	}
	_MergeSort(arr,a,0,n-1);
    free(a);
}
void _MergeSort(int*arr,int*a,int begin ,int end)
{
	//检验区间是否有效
	if (begin >= end)
	{
		return;
	}
	//后序遍历
	int mid = (begin + end) / 2;
	//新区间为[begin,mid],[mid+1,end]
	_MergeSort(arr,a,begin,mid);
	_MergeSort(arr,a,mid+1,end);
    //归并
	int begin1 = begin; int end1 = mid;
	int begin2 = mid+1; int end2 = end;
	//两个区间
	int index = begin;
	//新区间第一个元素的下标
	while (begin1 <= end1 && begin2 <= end2)
	{
		if (arr[begin1] <= arr[begin2])
		{
			a[index++] = arr[begin1++];
		}
		else
		{
			a[index++] = arr[begin2++];
		}
	}
	while (begin1 <= end1)
	{
		a[index++] = arr[begin1++];
	}
	while (begin2 <= end2)
	{
		a[index++] = arr[begin2++];
	}
	//将归并好的内容拷贝回原数组
	memcpy(arr+begin,a+begin,(end-begin+1)*sizeof(int));
}

2.5测试

归并排序含非递归版,数据结构与算法,算法

3.非递归实现归并排序 

根据我们之前的排序我们可以看到它的本质是和预排序差不多的形态,因此我们可以通过一个整型如gap来区分一个元素和一个元素排序,两个元素和两个元素排序.......

3.1初次实现

void MergeSortNonR(int* arr, int n)
{
	int* a = (int*)malloc(sizeof(int) * n);
	//创建辅助数组
	int gap = 1;
	//gap=1便是1个元素和1个元素归并
	while (gap < n)
	{
		int i = 0;
		for (i = 0; i < n; i += 2 * gap)
			//i+=2*gap跳过一整个区间
		{
			//两个区间
			int begin1 = i; int end1 = i + gap - 1;
			int begin2 = i + gap; int end2 = i + 2 * gap - 1;
			int index = i;	//新区间第一个元素的下标
			//归并
			while (begin1 <= end1 && begin2 <= end2)
			{
				if (arr[begin1] <= arr[begin2])
				{
					a[index++] = arr[begin1++];
				}
				else
				{
					a[index++] = arr[begin2++];
				}
			}
			while (begin1 <= end1)
			{
				a[index++] = arr[begin1++];
			}
			while (begin2 <= end2)
			{
				a[index++] = arr[begin2++];
			}
			memcpy(arr + i, a + i, sizeof(int) * (2 * gap));
		}
		gap *= 2;
	}
	free(a);
}

3.2测试 

出现了越界问题

归并排序含非递归版,数据结构与算法,算法

程序在打印之前就被迫中止了

归并排序含非递归版,数据结构与算法,算法 

3.3修改

观察可以发现,第一个区间的为[begin1,end1],第二个区间为[begin2,end2],那么begin1必然不会越界,而end1和更后面的begin2,end2都可能会越界。而其中end1和begin2越界的话都在说明已经排好序了,而end2越界,begin2没越界,则在说明还有数据未被排序。

再想一想细节,end1越界了,begin2一定越界,而begin2越界,end1不一定越界,所以无需考虑end1越界,只要begin2越界就说明排序已完成直接break   而只有end2越界,便将end2修正成数组的最大下标即可。

注意:我们之前使用拷贝函数均是拷贝2*gap个过去,在这里显然不合适,总区间长度应修正 为end2-begin1,这个修正不应该放在最后,因为在进行归并的期间,begin1会++至end1   也不应该放在判断begin2,end2越界之前,因为可能会对end2进行修正。综上所述,得出的代码为

void MergeSortNonR(int* arr, int n)
{
	int* a = (int*)malloc(sizeof(int) * n);
	//创建辅助数组
	int gap = 1;
	//gap=1便是1个元素和1个元素归并
	while (gap < n)
	{
		int i = 0;
		for (i = 0; i < n; i += 2 * gap)
			//i+=2*gap跳过一整个区间
		{
			//两个区间
			int begin1 = i; int end1 = i + gap - 1;
			int begin2 = i + gap; int end2 = i + 2 * gap - 1;
			if (begin2 >= n)
			{
				break;
			}
			if (end2 >= n)
				end2 = n - 1;//修正区间长度
			int order = end2 - begin1+1;//修正要拷贝的长度
			int index = i;	//新区间第一个元素的下标
			//归并
			while (begin1 <= end1 && begin2 <= end2)
			{
				if (arr[begin1] <= arr[begin2])
				{
					a[index++] = arr[begin1++];
				}
				else
				{
					a[index++] = arr[begin2++];
				}
			}
			while (begin1 <= end1)
			{
				a[index++] = arr[begin1++];
			}
			while (begin2 <= end2)
			{
				a[index++] = arr[begin2++];
			}
			/*int order = 2 * gap;
			if (2 * gap >= n)
			{
				order = n;
			}*/
			memcpy(arr + i, a + i, sizeof(int) * order);
		}
		gap *= 2;
	}
	free(a);
}

 3.4修改测试

归并排序含非递归版,数据结构与算法,算法

至此,归并排序的递归版和非递归版讲解完成,感谢各位友友的来访,祝各位友友前程似锦O(∩_∩)O 文章来源地址https://www.toymoban.com/news/detail-717114.html

到了这里,关于归并排序含非递归版的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 归并排序含非递归版

    目录 1.归并排序的原理  2.实现归并排序 2.1框架 2.2区间问题和后序遍历 2.3归并并拷贝 2.4归并排序代码 2.5测试 3.非递归实现归并排序  3.1初次实现 3.2测试  3.3修改  3.4修改测试 归并排序是建立在归并操作上的一种有效的排序算法,该算法是采用分治法的一个非常典型的应用。

    2024年02月08日
    浏览(31)
  • 【数据结构】非递归实现快速排序与归并排序

    递归是可以向非递归进行变化的: 比如很经典的 斐波那契数列 可以用 递归 实现也可以用 循环 实现 但是有些复杂的递归仅仅依靠循环是很难控制的, 所以我们需要借助数据结构中的 栈与队列 帮助我们用非递归模拟递归, 故有的时候我们说非递归不是递归却胜似递归 通过

    2024年01月21日
    浏览(38)
  • <数据结构>NO11.归并排序|递归|非递归|优化

    思路: 如果给出两个有序数组,我们很容易可以将它们合并为一个有序数组。 因此当给出一个无序数组时,我们先将它们均分为两组有序数组,在将这两组有序数组合并为一个有序数组;而将原数组分成2组 有序数组的思路又是归并排序 。 递归的结束条件是什么? 当递归区间

    2024年02月16日
    浏览(35)
  • 数据结构排序——详细讲解归并排序(c语言实现递归及非递归)

    上次是快排和冒泡:数据结构排序——详解快排及其优化和冒泡排序(c语言实现、附有图片与动图示意) 今天为大家带来归并排序 归并排序是一种分治算法,它将序列分成两个子序列,分别对 子序列进行排序 ,然后将排序好的子序列 合并起来 。这个过程可以 递归 地进行,

    2024年01月22日
    浏览(51)
  • 排序算法大全:冒泡排序【含优化】,选择排序【含优化】,直接插入排序,希尔排序,堆排序,快速排序【含3种实现版本及非递归实现】,归并排序【含非递归实现】。详细图解,文字解释,代码实现,性能分析。

    目录  一、冒泡排序 1、冒泡排序思想 2、冒泡排序算法的性能分析 二、选择排序 1、选择排序思想 2、选择排序算法的性能分析   三、直接插入排序 1、直接插入排序思想 2、直接插入排序算法的性能分析 四、希尔排序 1、希尔排序思想 2、希尔排序算法的性能分析 五、堆排

    2024年02月20日
    浏览(37)
  • 数据结构——排序算法——归并排序

    在第二个列表向第一个列表逐个插入的过程中,由于第二个列表已经有序,所以后续插入的元素一定不会在前面插入的元素之前。在逐个插入的过程中,每次插入时,只需要从上次插入的位置开始,继续向后寻找插入位置即可。这样一来,我们最多只需要将两个有序数组遍历

    2024年02月09日
    浏览(27)
  • 数据结构和算法笔记4:排序算法-归并排序

    归并排序算法完全遵循分治模式。直观上其操作如下: 分解:分解待排序的n个元素的序列成各具n/2个元素的两个子序列。 解决:使用归并排序递归地排序两个子序列。 合并:合并两个已排序的子序列以产生已排序的答案。 我们直接来看例子理解算法的过程,下面是要排序

    2024年01月21日
    浏览(46)
  • 数据结构与算法—归并排序&计数排序

    目录 一、归并排序 1、主函数 2、递归实现 3、优化递归  4、非递归实现 5、特性总结: 二、计数排序 1、代码: 2、特性总结: 三、各种排序总结 时间空间复杂度汇总  基本思想: 归并排序是建立在归并操作上的一种有效的排序算法,该算法是采用 分治法 的一个非常典型的

    2024年02月04日
    浏览(30)
  • 数据结构与算法:归并排序

    在讲解归并排序前,我们先看到一个问题: 对于这样两个有序的数组,如何将它们合并为一个有序的数组? 在此我们处理这个问题的思路就是:开辟一个新的数组,然后分别安置一个指针在左右数组,利用指针遍历数组,每次对比将比较小的那个元素插入到数组的尾部。 像

    2024年01月21日
    浏览(33)
  • 数据结构算法--5 归并排序

    我们先看一下归并排序是怎么归并的  两个有序列表,有low指针指向2,high指针指向6,mid指针指向9 再建一个新列表,12,所以1放到列表,右指针右移一位,再比较2和3,2放入列表,左指针右移一位,以此类推,肯定有一部分列表率先没有数,这时将另一列表直接append进入新

    2024年02月11日
    浏览(38)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包