【排序】快速排序的三种优化

对快速排序的优化

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);
}
  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值