快速排序/快速选择

文章讲述了在处理数组排序时,特别关注顺序数组和逆序数组的性能问题,强调了随机化选取pivot的重要性。介绍了快速排序、快速选择算法以及三路快排在寻找数组中第K个最大元素的应用,涉及LeetCode题目实例。
摘要由CSDN通过智能技术生成

实现细节(注意事项):(针对特殊测试用例:顺序数组或者逆序数组)一定要随机化选择切分元素(pivot),否则在输入数组是有序数组或者是逆序数组的时候,快速排序会变得非常慢(等同于冒泡排序或者「选择排序」)

随机化枢纽

快排代码模板(递归版):

class Solution {
public:
    void Qsort(vector<int>& nums, int L, int R){
        if(L>=R) return;
        //随机化取pivot
        int x = rand() % (R - L + 1) + L;
        swap(nums[L], nums[x]);
        int pivot = nums[L];
        
        int i=L, j=R;
        while(i<j){
            while(i<j && nums[j]>=pivot) j--;
            while(i<j && nums[i]<=pivot) i++;
            if(i<j) swap(nums[i],nums[j]);
        }
        swap(nums[L],nums[i]);
        Qsort(nums, L, i-1);
        Qsort(nums, i+1, R);
    }
    vector<int> sortArray(vector<int>& nums) {
        int size=nums.size();
        Qsort(nums, 0, size-1);
        return nums;
    }
};

快排思想升级版算法——快速选择(减治法)

模式识别:确定数据量的情况下寻找第K大的数

快速选择算法:快速排序算法中的轴值计算

class Solution {
public:
//所以只要某次划分的 q 为倒数第 k 个下标的时候,我们就已经找到了答案。 我们只关心这一点,至于 a[l⋯q−1]和 a[q+1⋯r]是否是有序的,我们不关心。

//⭐⭐
//因此我们可以改进快速排序算法来解决这个问题:在分解的过程当中,我们会对子数组进行划分
//如果划分得到的 q 正好就是我们需要的下标,就直接返回 a[q];
//否则,如果 q 比目标下标小,就递归右子区间,否则递归左子区间。这样就可以把原来递归两个区间变成只递归一个区间,提高了时间效率。这就是 ⭐「快速选择」算法。


//引入随机化来加速这个过程,它的时间代价的期望是 O(n)
//这个时间复杂度只有在 随机数据 下才成立,而对于精心构造的数据则可能表现不佳。因此我们这里并没有真正地使用随机数,而是使用双指针的方法,这种方法能够较好地应对各种数据。

//⭐快速选择
//以下为单个划分过程:
//整个快排过程的体现:不停的左右递归实现
    int partition(vector<int>& nums, int L, int R){
        int index = rand()%(R-L+1)+L;
        int pivot = nums[index];
        swap(nums[L],nums[index]);

        int i=L, j=R;
        while(i<j){
            while(i<j && nums[j]<=pivot) j--;
            while(i<j && nums[i]>=pivot) i++;
            if(i<j) swap(nums[i],nums[j]);
        }
        swap(nums[L],nums[i]);
        return i;
    }
    int findKthLargest(vector<int>& nums, int k) {
        int size = nums.size();
        int L=0,R=size-1;
        while(true){
            int index = partition(nums, L, R);
            if(index == k-1) return nums[index];
            else if(index<k-1) L=index+1;
            else R=index-1;
        }
    }
};

双指针

三路快排

class Solution {
public:
    int findKthLargest(vector<int>& nums, int k) {
        return quickSelect(nums, k);
    }
    
private:
    int quickSelect(vector<int>& nums, int k) {
        // 随机选择基准数
        int pivot = nums[rand() % nums.size()];
        // 将大于、小于、等于 pivot 的元素划分至 big, small, equal 中
        vector<int> big, equal, small;
        for (int num : nums) {
            if (num > pivot)
                big.push_back(num);
            else if (num < pivot)
                small.push_back(num);
            else
                equal.push_back(num);
        }
        // 第 k 大元素在 big 中,递归划分
        if (k <= big.size())
            return quickSelect(big, k);
        // 第 k 大元素在 small 中,递归划分
        if (nums.size() - small.size() < k)
            return quickSelect(small, k - nums.size() + small.size());
        // 第 k 大元素在 equal 中,直接返回 pivot
        return pivot;
    }
};

75. 颜色分类 - 力扣(LeetCode)

912. 排序数组 - 力扣(LeetCode)

215. 数组中的第K个最大元素 - 力扣(LeetCode)

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值