快速排序--随机pivot--双路快排--三路快排

排序数组

快速排序

快速排序是一种不稳定的排序,平均时间复杂度为O(N logN)的原地算法,最坏时间复杂度为O(N^2);
快速排序可以看成是构造一颗二叉搜索树,当大部分数据都在pivot的一边的时候,也就是都在一颗子树上时,就会导致最坏时间复杂度。
提升快速排序的性能的方法相当于使左右两棵子树尽量平衡。

随机选举pivot

如果只是选取特定数据的话,比如说auto pivot = nums[low],对于某些情况可能导致大部分数据都在pivot一边。
随机选举pivot的话,可以保证大部分情况左右两边数据数量差不多
代码实现如下

    int randomIdx = left + rand() % (right - left + 1);

    swap(nums[randomIdx], nums[left]);
    int pivot = nums[left];

双路快排

class Solution {
public:
    vector<int> sortArray(vector<int>& nums) {
        srand((unsigned)time(NULL));
        QuickSort(nums, 0, nums.size() - 1);

        return nums;
    }

    void QuickSort(vector<int>& nums, int left, int right) {
        if (left >= right) {return;}
        auto pivotIdx = partition(nums, left, right);

        QuickSort(nums, left, pivotIdx - 1);
        QuickSort(nums, pivotIdx + 1, right);
    }

    int partition(vector<int>& nums, int left, int right) {
        int randomIdx = left + rand() % (right - left + 1);
        swap(nums[randomIdx], nums[left]);
        int pivot = nums[left];
        
        //[left, le) less than pivot
        int le = left + 1;
        // (ge, right] greater than pivot
        int ge = right;

        while (true) {
            //为什么nums[le] < pivot, 是<,而不是<=
            //如果是<=, 则上下任意一个whlie循环遇到一段连续相等的值的话,
            //循环不会停下,导致一侧数量过多
            while (le <= ge && nums[le] < pivot) {
                le++;
            }

            while (le <= ge && nums[le] < pivot) {
                ge--;
            }

            if (le >= ge) {
                break;
            }

            swap(nums[le++], nums[ge--]);
        }
        swap(nums[ge], nums[left]);
        
        return ge;
    }
};

难点解析

  1. 把握循环不变量

[left, le) less than pivot
(ge, right] greater than pivot
由上述关系确定最终状态时le``ge指针的位置

  1. nums[le] < pivot
    nums[le] < pivot
    死循环中的两个循环的条件是<
    如果是<=,会导致连续相等的元素都在一边
    从而降低效率

过程分析

初始状态如下
image.png
跳出两个指针le``ge循环后,交换两个元素前
image.png

交换后应该分别递增两个指针
image.png

跳出while死循环后,根据循环不变量可知([left, le) less than pivot(ge, right] greater than pivot)如下图关系,则需交换geleft所指向的元素。
image.png

三路快排

void sortThree(vector<int>& nums, int left, int right) {
        if (left >= right) { return; }

        int randomIdx = left + rand() % (right - left + 1);
        swap(nums[randomIdx], nums[left]);
        int pivot = nums[left];

        //[left...lt) less than pivot 
        int lt = left;
        //[lt, i) equal to pivot
        int i = left + 1;
        //(gt, right] great than pivot
        int gt = right;

        while (i <= gt) {
            if (nums[i] > pivot) {
                swap(nums[i], nums[gt--]);
            } else if (nums[i] == pivot) {
                i++;
            } else {
                swap(nums[lt++], nums[i++]);
            }
        }

        sortThree(nums, left, lt - 1);
        sortThree(nums, gt + 1, right);
    }

难点分析

循环不变量—见双路快排

过程分析

当代码刚进入while循环的表示图如下。
image.png
如果nums[i] > pivot,则表明i所指的方块为绿色,交换gt和i所指的位置,i所指的方块就是原来gt所指的白色,所以i不用递增,gt所指方块是原来i所指的,后来判断为绿色的区域,所以递减gt;
image.png
image.png
如果nums[i] == pivot则i所指的方块为红色,只需要递增i即可
image.png
如果nums[i] < pivot则i所指的方块为黄色,交换i和lt所指的方块后,lt所指的方块为黄色,需要递增lt,i所指的方块为红色,需要递增i。
image.png
image.png
仔细分析代码可以知道,未进入循环前,有一个left==lt所指向的格子是红色的,也就是pivot所代表的格子。
image.png

参考

算法四

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值