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

🍬个人主页:Yanni.—

🌈数据结构:Data Structure.​​​​​​

🎂C语言笔记:C Language Notes

🏀OJ题分享: Topic Sharing


目录

前言:

非递归基础思想

快速排序非递归思路

快速排序非递归实现

归并排序的非递归思路

归并排序的非递归实现


前言:

在之前学习了快速排序和归并排序,但算法就是用递归实现的,在企业的面试中,很多企业不会问你快速排序和归并排序递归算法的思想,而是非递归实现这两个排序,今天为大家带来两个排序算法的非递归实现。

非递归基础思想

每个算法都有好有坏,递归算法的优势在于代码简洁,时间效率高。同时递归也是由缺点的:

栈帧深度太深,导致栈空间不够用,栈可能会溢出,当递归的次数到达一个很大的数值时,这时候开辟栈帧的空间就不够用了。

那么怎么将递归变成非递归,这里有两种方法。

1.直接将递归的思路改为循环来实现,不过只适用于简单的代码(比如就1+2+...+n的和)。

2.借助数据结构中的栈(队列)来模拟递归的过程。

第二种方法是今天将两个排序算法变成非递归的关键。

快速排序非递归思路

这是快速排序的基本思路,之前我们用了递归的思想来实现它,递归的本质是将Key值的左右区间存放起来,那么我们借助数据结构栈也可以存放。

每进行一步操作,都会用挖坑法(左右指针法和前后指针法均可,前文有详细讲解)中间取值,完成区间排序。当栈的元素全部被取完,排序完毕。

快速排序非递归实现

void QuickSortNonR(int* a, int n)
{
	Stack st;
	StackInit(&st);
	StackPush(&st, n - 1);
	StackPush(&st, 0);
	while (!StackEmpty(&st))//当栈为空 则循环停止,排序完成
	{
		int left = StackTop(&st);
		StackPop(&st);

		int right = StackTop(&st);
		StackPop(&st);

		int KeyIndex = PartSort1(a, left, right);//挖坑法
		//在这里必须从序列尾部压栈,因为栈是先进后出
		if (KeyIndex + 1 < right)
		{
			StackPush(&st, right);
			StackPush(&st, KeyIndex + 1);
		}
		if (KeyIndex - 1 > left)
		{
			StackPush(&st, KeyIndex - 1);
			StackPush(&st, left);
		}
		
	}
	StackDestory(&st);
}

归并排序的非递归思路

实现归并排序的非递归我们利于循环来解决。

1.将8个数字以2为单位排序合并。

2.以4为单位将左右区间排序合并。

3.以8为单位将左右区间排序合并。

单位的变化可以用gap变量来实现,创造出一个循环。这是基本的思路,要实现非递归我们还需要进行条件优化。

当序列变为9个,10个那么两两位一组必定会发生越界访问。

要解决这个问题我们得将尾端合理化

//归并过程中的右半区间不存在
if (begin2 >= n)
	break;
//归并过程中的右半区间算多了
if (end2 >= n)
{
	end2 = n - 1;
}

归并排序的非递归实现

void MergeSortNonR(int* a, int n)
{
	int* tmp = (int*)malloc(sizeof(int) * n);
	if (tmp == NULL)
	{
		printf("malloc fail!");
	}
	int gap = 1;
	while (gap < n)
	{
		for (int 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)
				break;
			//归并过程中的右半区间算多了
			if (end2 >= n)
			{
				end2 = n - 1;
			}
			int index = i;
			while (begin1 <= end1 && begin2 <= end2)
			{
				if (a[begin1] > a[begin2])
				{
					tmp[index++] = a[begin2++];
				}
				else {
					tmp[index++] = a[begin1++];
				}
			}
			while (begin1 <= end1)
			{
				tmp[index++] = a[begin1++];
			}
			while (begin2 <= end2)
			{
				tmp[index++] = a[begin2++];
			}
		}
		gap *= 2;
		//拷贝回去
		for (int j = 0; j < n; j++)
		{
			a[j] = tmp[j];
		}
	}
	free(tmp);
	
}

好啦,这就是今天学习的分享啦!看到希望大家的三连呀!

如果有不当之处,欢迎大佬指正!

  • 8
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值