快速排序-加餐

1.快排性能的关键点分析

决定快排性能的关键点是每次单趟排序后,key对数组的分割,如果每次选的key基本都二分居中,那么快排的递归树就是一棵均匀的满二叉树,性能达到最佳。

但是在实践中虽然不可能每次都是二分居中,但是性能也是可控的,但是如果每次选到最小或者最大值,就会划分成0和N-1个子问题,时间复杂度就会变成O(N^2),就比如数组是有序的就会出现这种情况。

在之前相关的章节,我们用了三数取中随机选取key和小区间优化来解决这个问题,虽然解决了大多数问题,但是还是有一些场景没能解决,就比如数组中有大量重复的数据。

一下的措施可以进一步的优化我们的快速排序。

2.优化的方法

2.1三路划分

当面对有着大量跟key相同的值时,三路划分的核心思想就是把数组中的数据划分为三段(比key小的,跟key相等的,比key大的)。

基本步骤:

1.key默认取left位置的值(需要记录key的值)

2.left指向区间的最左边,right指向区间的最右边,cur指向left+1的位置

3.如果cur的值比key小,交换cur和left的数据位置,left++,cur++

4.如果cur的值比key大,交换cur和right的数据位置,right--

5.如果cur的值和key相等,cur++

6.cur大于right结束

. - 力扣(LeetCode)

2.2lomuto的快排

这里指的就是前后指针法,老铁们看过我之前的数据结构-排序2的快排那一节就知道了。

上述leetcode用的就是lomuto的快排加上随机取key的方法实现的。

2.3introsort的快排

introsort是introspective sort的缩写,实现思路就是进行自我侦测和反省,快排递归深度太深(sgi STL中使用的是深度为2倍排序元素数量的对数值,这里是指C++里使用的场景)那就说明在这种数据序列下,选key出现了问题,性能在快速退化,那么就不要再进行快排分割递归了,改换为堆排序进行排序。 

void Swap(int* p1,int* p2)

{

    int tmp = *p1;

    *p1 = *p2;

    *p2 = tmp;

}

//选择排序

 void InsertSort(int* a,int n)

 {

    for(int i = 0;i < n-1;i++)

    {

        int end = i;

        int tmp = a[end + 1];

        while(end >= 0)

        {

            if(tmp < a[end])

            {

                a[end+1] = a[end];

                --end;

            }

            else

            {

                break;

            }

        }

        a[end+1] = tmp;

    }

 }

 void AdjustDown(int* a, int n, int parent)

{

    //先假设左孩子小

    int child = parent * 2 + 1;

    while (child < n) //child >= n 说明孩子不存在,调整到叶子了

    {

        //找到小的那个孩子

        if ((child + 1) < n && a[child + 1] > a[child])//防止右孩子的越界风险

        {

            ++child;

        }

        if (a[child] > a[parent])

        {

            Swap(&a[child], &a[parent]);

            parent = child;

            child = parent * 2 + 1;

        }

        else

        {

            break;

        }

    }

}

//堆排序

void HeapSort(int* a, int n)

{

    for (int i = (n - 1 - 1) / 2; i >= 0; i--)

    {

        AdjustDown(a, n, i);//向下调整建堆 O(N)

    }

    //O(N*logN)

    int end = n - 1;

    while (end > 0)

    {

        Swap(&a[0], &a[end]);

        AdjustDown(a, end, 0);

        --end;

    }

}

//三数取中

int GetMidi(int* a, int left, int right)

{

    int midi = (left + right) / 2;

    //left midi right

    if (a[left] < a[midi])

    {

        if (a[midi] < a[right])

        {

            return midi;

        }

        else if (a[left] < a[right])

        {

            return right;

        }

        else

        {

            return left;

        }

    }

    else  //a[left] > a[midi]

    {

        if (a[midi] > a[right])

        {

            return midi;

        }

        else if (a[left] > a[right])

        {

            return right;

        }

        else

        {

            return left;

        }

    }

}

//自省排序

void IntroSort(int* a,int left,int right,int depth,int defaultDepth)

{

    if(left >= right)

        return;

    //数组长度小于16的小数组,换为插入排序,简单递归次数

    if((right - left) + 1 < 16)

    {

        InsertSort(a+left,right - left + 1);

        return;

    }

    //当深度超过2*logN,则改用堆排序

    if(depth > defaultDepth)

    {

        HeapSort(a+left,right - left + 1);

        return;

    }

    depth++;

    int begin = left;

    int end = right;

    //三数取中

    int mid = GetMidi(a,left,right);

    Swap(&a[left],&a[mid]);

    int keyi = left;

    int prev = left;

    int cur = prev + 1;

    while(cur <= right)

    {

        if(a[cur] < a[keyi] && ++prev != cur)

            Swap(&a[prev],&a[cur]);

       

        cur++;

    }

    Swap(&a[prev],&a[keyi]);

    keyi = prev;

    //[begin,keyi-1] keyi [keyi+1,end]

    IntroSort(a,begin,keyi-1,depth,defaultDepth);

    IntroSort(a,keyi+1,end,depth,defaultDepth);

}

//快排

void QuickSort(int* a,int left,int right)

{

    int depth = 0;

    int logn = 0;

    int N =right - left + 1;

    for(int i = 1;i < N;i *= 2)

    {

        logn++;

    }

    //自省排序

    IntroSort(a,left,right,depth,logn*2);

}

int* sortArray(int* nums, int numsSize, int* returnSize)

{

    srand(time(0));

    QuickSort(nums,0,numsSize-1);

    *returnSize = numsSize;

    return nums;

}

自省排序

. - 力扣(LeetCode)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值