目录
一、快排(递归版)改进
1、背景
在上一个 part 中我们留了个坑:
就是随着快排递归深度的增加,数组就会越来越有序,那么就会降低快排的效率。而且,递归到最底层时需要排序的数组个数几乎占总体的一半。因此,为了解决这一问题,我们可以当数组长度到达某一个特定的值时,直接用插入排序对这个数组排序。
2、代码
void QuickSort(int* a, int left, int right)
{
if (left >= right)
return;
/* 当长度小于10时,改用插入排序 */
if (right - left + 1 < 10)
{
InsertSort(a + left, right - left + 1);
return;
}
int ret = PartSort2(a, left, right);
QuickSort(a, left, ret - 1);
QuickSort(a, ret + 1, right);
}
二、快排(非递归版)
1、背景
由于有时递归次数过多会栈溢出,而非递归就不会,因此我们也要学会把递归的函数改成非递归的形式。
2、思路
因为快排的思想是先确定key最终的位置,然后再对左右部分进行子问题递归,因此这个过程与二叉树的前序遍历非常相似。再者,快排递归的过程就是一个建立栈帧销毁栈帧的过程,因此数据结构的栈毫无疑问是模拟函数栈帧的不二人选了。
由于在递归版的快排中,每个栈帧传的是数组的下标,因此在用栈模拟时,我们应该也把每次需要排序的下标区间存到栈里面。
3、代码
void QuickSortNonR(int* a, int left, int right)
{
ST stack;
STInit(&stack);
/* 先放入待排序的区间 */
STPush(&stack, right);
STPush(&stack, left);
/* 当栈为空时,整个快排就完成了 */
while (!STEmpty(&stack))
{
int l = STTop(&stack);
STPop(&stack);
int r = STTop(&stack);
STPop(&stack);
/* 单趟快排 */
int ret = PartSort3(a, l, r);
if (r > ret + 1)
{
STPush(&stack, r);
STPush(&stack, ret + 1);
}
if (l < ret - 1)
{
STPush(&stack, ret - 1);
STPush(&stack, l);
}
}
STDestroy(&stack);
}