对快速排序的优化
1.三数取中优化
原因:若对接近有序的数组进行快速排序,每一次key取开头的数都是最小的,那么每一次比key大的数都在key的右边;进行递归时只有对右边递归。这种情况下,若有N个数则递归的次数接近N方。
因此通过三数取中,通过比较begin ,end和mid位置三个数,得到中位数,每一次递归调用快排,把中位数置于key的位置。防止对接近有序数组排序时多次递归。
int findmid(int *a, int begin, int end) //三数取中
{
int mid = (begin + end) / 2;
if (a[mid] > a[begin]) //当mid大于begin的时候
{
if (a[mid] < a[end]) // mid小于end,则此时中位数为mid
{
return mid;
}
else if (a[end] > a[begin]) // 由于上一步判断,此时end小于mid,若end大于begin,则中位数为end
{
return end;
}
else // end大于mid,end小于begin,则中位数为begin
{
return begin;
}
}
else //当begin大于等于 mid的时候
{
if (a[begin] < a[end]) //若begin小于end,则中位数为begin
{
return begin;
}
else if (a[end] > a[mid]) // end小于begin,end大于mid,中位数为end
{
return end;
}
else // end小于mid,mid小于end,中位数则为mid
{
return mid;
}
}
}
int partsort3(int *a, int begin, int end)
{
int key = begin;
int prev, cur;
prev = begin;
cur = begin + 1;
//加入三数取中优化
int mid = findmid(a, begin, end);
Swap(&a[key], &a[mid]);//每一次快排前先把中位数置于数组头位置。
while (cur <= end)
{
if (a[cur] < a[key] && ++prev != cur)
{
Swap(&a[cur], &a[prev]);
}
cur++;
}
Swap(&a[key], &a[prev]);
key = prev;
return key;
}
2.减少递归次数
尽管每一次快排,都是把数组2分,递归。如果数组元素N很大时,如果深度太深,会出现栈溢出。每次把数组2分,到最后会分出很多个小数组。容易栈溢出。我们可以利用其他的排序方法,在数组元素个数小于(10-20个)的时候不再利用递归,而是利用插入排序
void quicksort(int *a, int begin, int end)
{
if (begin >= end)
{
return;
}
if (end - begin > 20)
{
int key = partsort3(a, begin, end);//调用前后指针快排
quicksort(a, begin, key - 1);
quicksort(a, key + 1, end);
}
else
{
insertsort(a + begin, end + 1);调用 插入排序
}
}
3.非递归快排序
若使用递归,极端场景下面数组的元素个数N很大,若使用递归思想,如果深度太深,会出现栈溢出。因此我们还有一种方法就是用非递归。利用栈的思路
栈非递归
思路:利用栈来存储需要排序数组的左右边界
1.我们一开始先把数组的end入栈,再入begin。
2.(当栈不为空)进入循环,把begin赋给left然后出栈,接着把end赋给right,然后出栈。通过快排得到key。
3.此时需要key-1,和left入栈;和right和key+1入栈
void Quicksort(int *a, int begin, int end)
{
ST st;
StackInit(&st);
StackPush(&st, end);//第一步1.我们一开始先把数组的end入栈,
StackPush(&st, begin);///再入begin。
while (!StackEmpty(&st))
{
int left = StackTop(&st);//取待排序区间的左边界值下标
StackPop(&st);
int right = StackTop(&st);//取待排序区间的有边界值下标
StackPop(&st);
int key = partsort3(a, left, right);//快排得到key的下标
if (left < key - 1)//把key左边区间的左右边界值入栈
{
StackPush(&st, key - 1);//入右边界值
StackPush(&st, left);//入左边界值
}
if (key + 1 < right)//把key右边区间的左右边界值入栈
{
StackPush(&st, right);//入右边界值
StackPush(&st, key + 1);//入左边界值
}
}
StackDestroy(&st);
}
队列非递归
队列先进先出
void Quicksort2(int *a, int begin, int end)
{
Queue q;
QueueInit(&q);
QueuePush(&q, begin);
QueuePush(&q, end);
while (!QueueEmpty(&q))
{
int left = QueueFront(&q);
QueuePop(&q);
int right = QueueFront(&q);
QueuePop(&q);
int key = partsort3(a, left, right);
if (left < key - 1)
{
QueuePush(&q, left);
QueuePush(&q, key - 1);
}
if (key + 1 < right)
{
QueuePush(&q, key + 1);
QueuePush(&q, right);
}
}
QueueDestroy(&q);
}