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

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

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

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

快速排序的非递归:

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

注意:下图所使用的单趟排序为前后指针法----前后指针法所在文章。
在这里插入图片描述
注意

  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);
}

归并排序的非递归:

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

因为我们发现:

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

而归并排序的思想呢?
在这里插入图片描述
的时候只是完成了分组,每一组是返回条件的最小单元,还没有进行归并,
归并的过程是在分组之后完成的,是一种后序的思想

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

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

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

在这里插入图片描述
这张图就比较好的体现了非递归的思想,直接进行归并,这就要求我们要对数组的下标使用的比较灵活。

我们使用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;
	}
	... 接下来是进行两者合并的逻辑
}

这是两个”数组”进行合并的逻辑

	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);
}
  • 19
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值