【数据结构】非递归实现快速排序与归并排序

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

递归是可以向非递归进行变化的:
比如很经典的斐波那契数列可以用递归实现也可以用循环实现

但是有些复杂的递归仅仅依靠循环是很难控制的,
所以我们需要借助数据结构中的栈与队列帮助我们用非递归模拟递归,
故有的时候我们说非递归不是递归却胜似递归

通过本文可以更好的对比来理解两者不同之处

快速排序的非递归:

先说结论,我们会使用来模拟快速排序的递归----栈所在的文章。

注意:下图所使用的单趟排序为前后指针法----前后指针法所在文章。
【数据结构】非递归实现快速排序与归并排序,基于C语言实现的数据结构,数据结构,排序算法,算法
注意

  1. 我们选择先压右边,这样StackTop得到的就是左边,因为栈先进后出的原理
  2. 虽然我们自己造的栈push一次只能存储一个数据,但是我们push两次就可以解决这个问题

图示就很好的展示了如何用栈来模拟递归的过程。
可以与代码相互进行加强理解:

代码:

//前后指针
int PartSort(int* a, int left, int right)
{
	int prev = left;
	int cur = left + 1;
	int key = a[left];

	while (cur <= right)
	{
		if (a[cur] < key)
		{
			prev++;
			Swap(&a[prev], &a[cur]);
		}
		cur++;
	}
	Swap(&a[prev], &a[left]);
	return prev;
}

void QuickSortNonR(int* a, int begin, int end)
{
	Stack s;
	StackInit(&s);
	StackPush(&s, end);
	StackPush(&s, begin);

	while (!StackEmpty(&s))
	{
		int left = StackTop(&s);
		StackPop(&s);
		int right = StackTop(&s);
 		StackPop(&s);
		int keyi = PartSort(a, left, right);

		//[begin keyi-1] [keyi+1 right]
		if (keyi + 1 < right)
		{
			StackPush(&s, right);
			StackPush(&s, keyi + 1);
		}
		if (left < keyi - 1)
		{
			StackPush(&s, keyi - 1);
			StackPush(&s, left);
		}
	}

	StackDestroy(&s);
}

归并排序的非递归:

那么归并是否也是使用栈来模拟呢?
答案是否定的,同时队列也是错误的

因为我们发现:

快排的模拟递归是前序,即下来的过程就完成了排序,不需要回去。

而归并排序的思想呢?
【数据结构】非递归实现快速排序与归并排序,基于C语言实现的数据结构,数据结构,排序算法,算法
的时候只是完成了分组,每一组是返回条件的最小单元,还没有进行归并,
归并的过程是在分组之后完成的,是一种后序的思想

故我们当然不能用栈来模拟归并,即使可以达到分组的效果,那之后呢?

这里我们选择使用数组,需要一个额外的数组,空间复杂度仍然是O(N)

那我们是如何进行模拟的呢?

【数据结构】非递归实现快速排序与归并排序,基于C语言实现的数据结构,数据结构,排序算法,算法
这张图就比较好的体现了非递归的思想,直接进行归并,这就要求我们要对数组的下标使用的比较灵活。

我们使用gap作为每一组(有序的组)的元素个数
先来看一次归并的代码,虽然长,但是分开来看就比较一目了然

这一部分是对下标的控制,
我们完成这一部分再嵌套一个控制gap的循环就得到完整的代码
int gap = 1;
int j = 0;
for (int i = 0; i < n; i += (2 * gap))
{
	int begin1 = i, end1 = i + gap - 1;
	int begin2 = end1 + 1, end2 = begin2 + gap - 1;
	
	这一部分是防止数组越界的操作,
	当end1与begin2越界我们break即可
	因为当上两者越界时,我们所在的区间已经有序,不需要排序
	if (end1 >= n || begin2 >= n)
	{
		break;
	}
	if (end2 >= n)
	{
		end2 = n - 1;
	}
	... 接下来是进行两者合并的逻辑
}

这是两个”数组”进行合并的逻辑文章来源地址https://www.toymoban.com/news/detail-813140.html

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

代码:

void MergerSortNonR(int* a, int n)
{
	int* tmp = (int*)malloc(sizeof(int) * n);
	if (tmp == NULL)
	{
		perror("malloc fail");
		return;
	}

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

			if (end1 >= n || begin2 >= n)
			{
				break;
			}
			if (end2 >= n)
			{
				end2 = n - 1;
			}

			while (begin1 <= end1 && begin2 <= end2)
			{
				if (a[begin1] < a[begin2])
				{
					tmp[j++] = a[begin1++];
				}
				else
				{
					tmp[j++] = a[begin2++];
				}
			}
			while (begin1 <= end1)
			{
				tmp[j++] = a[begin1++];
			}
			while (begin2 <= end2)
			{
				tmp[j++] = a[begin2++];
			}
			memcpy(a + i, tmp + i, sizeof(int) * (end2 - i + 1));
		}
		gap *= 2;
	}
	free(tmp);
}

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

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

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

相关文章

  • <数据结构>NO11.归并排序|递归|非递归|优化

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

    2024年02月16日
    浏览(34)
  • 【数据结构】手撕归并排序(含非递归)

    目录 一,归并排序(递归) 1,基本思想  2,思路实现 二,归并排序(非递归) 1,思路实现 2,归并排序的特性总结: 1,基本思想 归并排序 (MERGE-SORT) 是建立在 归并操作 上的一种 有效的排序算法 ,该算法是采用 分治法(Divide and Conquer) 的一个非常典型的应用; 将 已

    2024年02月08日
    浏览(36)
  • 数据结构进阶篇 之 【交换排序】(冒泡排序,快速排序递归、非递归实现)详细讲解

    当你觉的自己不行时,你就走到斑马线上,这样你就会成为一个行人 1.1 基本思想 1.2 实现原理 1.3 代码实现 1.4 冒泡排序的特性总结 2.1 基本思想 2.2 递归实现 2.2.1 hoare版 2.2.2 前后指针版本 2.3 快速排序优化 2.3.1 随机数选key 2.3.2 三数取中选key 2.3.3 递归到小的子区间使用插入排

    2024年04月10日
    浏览(52)
  • 数据结构:直接插入排序,希尔排序,选择排序,堆排序,冒泡排序,快速排序,归并排序,计数排序(C实现)

    个人主页 : 个人主页 个人专栏 : 《数据结构》 《C语言》 排序:使一串数据,按照其中的某个或某些的大小,递增或递减的排列起来的操作。 插入排序的思路:把待排序数组,逐个插入到已经排好序的有序数组中,直到所有待排序数组插入完成,的到一个新的有序

    2024年02月11日
    浏览(32)
  • 数据结构__<八大排序> __插入排序 |希尔排序 |选择排序 |堆排序 |快速排序 |归并排序(C语言实现)

    目录 插入排序  希尔排序 选择排序 堆排序 冒泡排序 快速排序 hoare法 挖坑法 前后指针法 快排特性总结 三数取中优化 小区间优化 快排非递归 归并排序 归并排序非递归 计数排序 总结 OJ测试 1、元素集合越接近有序,直接插入排序算法的时间效率越高 2、时间复杂度:O(N^2

    2023年04月09日
    浏览(61)
  • 【数据结构与算法】快速排序的非递归实现方法

      目录 一.前言 二.非递归实现 如果数据量过大的话,不断递归就会出现 栈溢出 的现象,这个时候你的代码是没问题的,但就是跑不起来,这个时候就要 把递归改成非递归 。 一般有两种改法: 1.直接改,利用循环等; 2.借助栈的辅助。 而快速排序的非递归实现方法就需要

    2023年04月17日
    浏览(41)
  • 【数据结构】快速排序,归并排序

    1.hoare版本 根据动图的演示,整理的思路如下, 1.定义left,right,key。key默认是左边第一个元素,像两个指针,左边找比key大的,右边找比k小的,找到的话,交换二者,往返这个过程,当left与right相遇时,交换key和此时相遇的值. 单趟下来,6出现在正确的位置。 1.为什么大循环

    2024年01月20日
    浏览(30)
  • 【数据结构与算法】如何对快速排序进行细节优化以及实现非递归版本的快速排序?

    君兮_的个人主页 即使走的再远,也勿忘启程时的初心 C/C++ 游戏开发 Hello,米娜桑们,这里是君兮_,国庆长假结束了,无论是工作还是学习都该回到正轨上来了,从今天开始恢复正常的更新频率,今天为大家带来的内容是快速排序的两大优化和非递归实现 好了废话不多说,开

    2024年02月08日
    浏览(37)
  • 【数据结构初阶】十、快速排序(比较排序)讲解和实现(三种递归快排版本 + 非递归快排版本 -- C语言实现)

    ========================================================================= 相关代码gitee自取 : C语言学习日记: 加油努力 (gitee.com)  ========================================================================= 接上期 : 【数据结构初阶】九、排序的讲解和实现(直接插入 希尔 直接选择 堆 冒泡 -- C语言)

    2024年02月08日
    浏览(35)
  • 【数据结构】 七大排序详解(贰)——冒泡排序、快速排序、归并排序

    ==冒泡排序(Bubble Sort)==也是一种简单直观的排序算法。它重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。这个算法的名字由来是因为越小的元素会

    2024年02月09日
    浏览(41)

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

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

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

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

二维码1

领取红包

二维码2

领红包