前面我们已经写了快排用递归的方法实现,在数据量大的时候,有可能会栈溢出。这里我们尝试一下改为非递归。
区分:
- 数据结构的栈——利用的是内存中的堆空间
- 内存的栈——利用就是内存中的栈空间——函数创建函数栈帧
- 堆的空间是远远大于栈的空间
递归 -> 非递归
- 循环 一般递归可以改为非递归的话,就说明这个题目用循环来写会更顺。有些题目不好改为非递归,比如二叉树的前序遍历。
- 借助栈。
基本思路
注意栈后进先出的特性。
- 首先入栈单趟排序的区间,然后出栈确定left和right,单趟排序确定keyi。接下来根据新的keyi再次入栈单趟排序的区间。
- 对于区间,先入end,再入begin。先出begin,再出end。对于子问题,先入右序列,再入左序列。先出左序列,再出右序列。(对应于递归先递归左边再递归右边。)
- 递归:把子问题放到函数栈帧(借助栈的空间)
- 非递归:把子问题放到数据结构的栈(借助堆的空间)
- 不是递归胜似递归。
代码实现
- 入栈的条件left < right。
- 栈不为空就继续,栈为空就结束。
- 对于区间,先入end,再入begin。先出begin,再出end。对于子问题,先入右序列,再入左序列。先出左序列,再出右序列。
- 一定要记得赋值完left和right后要出栈
void QuickSortNonR(int* a, int begin, int end)
{
ST s;
STInit(&s);
STPush(&s, end); //先进end
STPush(&s, begin); //再进begin
while (!STempty(&s))
{
int left = STTop(&s); //取begin为left
STPop(&s); //begin出栈
int right = STTop(&s); //取end为right
STPop(&s); //end出栈
int keyi = PartSort3(a, left, right); //单趟排序取新keyi
// [left, keyi-1] keyi [keyi+1, right]
//如果子区间不是left==right或者无数据
if (keyi + 1 < right)
//处理右区间
{
STPush(&s, right); //先入end
STPush(&s, keyi + 1); //再入begin
}
if (left < keyi - 1)
//处理左区间
{
STPush(&s, keyi - 1); //先入end
STPush(&s, left); //再入begin
}
}
STDestroy(&s);
}