递归是可以向非递归进行变化的:
比如很经典的斐波那契数列
可以用递归
实现也可以用循环
实现
但是有些复杂的递归仅仅依靠循环是很难控制的,
所以我们需要借助数据结构中的栈与队列
帮助我们用非递归模拟递归,
故有的时候我们说非递归不是递归却胜似递归
通过本文可以更好的对比来理解两者不同之处
先说结论,我们会使用栈
来模拟快速排序的递归----栈所在的文章。
注意:下图所使用的单趟排序为前后指针法
----前后指针法所在文章。
注意:
StackTop
得到的就是左边,因为栈先进后出
的原理图示就很好的展示了如何用栈来模拟递归的过程。
可以与代码相互进行加强理解:
//前后指针
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);
}