一.为何“快排”变“慢排”
我们知道,快排是一种很好的排序算法。但是在极少数的一些情况下,“快速排序”好像名不副实了。
当数据量非常大,且递归深度太深,有栈溢出的风险。
这样,我们就得到了一个很可惜的结论:快排不是万金油。
但是,这是指的递归版本的快排,我们可以写非递归方式的快速排序,来看看是否能解决这个问题。
二.更改思路
想要改成非递归的版本,那么循环是否可以呢?
这里涉及到了左右区间分别递归,想要用循环实现,还是不大容易的。
//快速排序
void QuickSort(int* a, int left,int right)
{
if (left >= right)
{
return;
}
int keyi = Partion(a, left, right);
QuickSort(a, left, keyi - 1);
QuickSort(a, keyi + 1, right);
}
那么,我们还有其它方式来解决这个问题吗?
答案是当然有。
我们可以用栈模拟。
栈中存储的是需要排序的区间。栈是后入先出的。
如果是C++,那我们可以直接用STL里的,但是由于我们这里选择使用C语言来实现,所以我们需要先写一个栈。
下面是我们简单实现的一个栈。
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
#include<stdbool.h>
typedef int STDataType;
typedef struct Stack
{
STDataType* a;
int top; //栈中数据数量
int capacity; //栈的容量
}ST;
//检查空间是否足够,不够则扩容
void CheckCapacity(ST* ps)
{
if (ps->top == ps->capacity)
{
int newcapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;
STDataType* tmp = realloc(ps->a, sizeof(STDataType) * newcapacity);
if (tmp == NULL)
{
printf("realloc failed!\n");
exit(-1);
}
ps->a = tmp;
ps->capacity = newcapacity;
}
}
//初始化
void StackInit(ST* ps)
{
assert(ps);
ps->a = NULL;
ps->top = 0;
ps->capacity = 0;
}
//销毁
void StackDestroy(ST* ps)
{
assert(ps);
free(ps->a);
ps->a = NULL;
ps->capacity = ps->top = 0;
}
//插入数据
void StackPush(ST* ps, STDataType x)
{
assert(ps);
CheckCapacity(ps);
ps->a[ps->top] = x;
ps->top++;
}
//删除数据
void StackPop(ST* ps)
{
assert(ps);
assert(ps->top > 0);
ps->top--;
}
//取出栈顶数据
STDataType StackTop(ST* ps)
{
assert(ps);
assert(ps->top > 0);
return ps->a[ps->top - 1];
}
//计算栈中数据数量
int StackSize(ST* ps)
{
assert(ps);
return ps->top;
}
//判断栈是否为空
bool StackEmpty(ST* ps)
{
assert(ps);
return ps->top == 0;
}
三.具体实现
我们可以使用栈来模拟递归过程:
每次从栈中取出一段区间,单趟分割处理左右子区间入栈。
#include"sort.h"
#include"Stack.h"
void Swap(int* p, int* q)
{
int tmp = *p;
*p = *q;
*q = tmp;
}
//递归实现
int PartSort1(int* a, int left, int right)
{
int keyi = left;
while (left < right)
{
while (a[right] >= a[keyi] && left < right)
{
right--;
}
while (a[left] <= a[keyi] && left < right)
{
left++;
}
Swap(&a[left], &a[right]);
}
Swap(&a[left], &a[keyi]);
return left;
}
void QuickSort1(int* a, int left, int right)
{
if (left >= right)
{
return;
}
int keyi = PartSort1(a, left, right);
QuickSort(a, left, keyi - 1);
QuickSort(a, keyi + 1, right);
}
//非递归实现
void QSortNonR(int* a, int begin, int end)
{
ST st;
StackInit(&st);
StackPush(&st, end);
StackPush(&st, begin);
while (!StackEmpty(&st))
{
int left = StackTop(&st);
StackPop(&st);
int right = StackTop(&st);
StackPop(&st);
int keyi = PartSort1(a, left, right);
if (keyi + 1 < right)
{
StackPush(&st, right);
StackPush(&st, keyi + 1);
}
if (left < keyi - 1)
{
StackPush(&st, keyi - 1);
StackPush(&st, left);
}
}
StackDestroy(&st);
}