• 「数组」随机快速选择 / LeetCode LCR 076(C++)


    前置知识

    在本篇文章之前,你应该先掌握快速排序的基本技巧,详见:「数组」快速排序 / 随机值优化|小区间插入优化(C++)

    概述 

    LeetCode LCR 076是这么一道题:

    给定整数数组 nums 和整数 k,请返回数组中第 k 个最大的元素。

    请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。

    示例 :

    输入: [3,2,1,5,6,4] 和 k = 2输出: 5

    这样的题目可以直接通过快速选择进行完全排序,但时间复杂度是O(nlogn)。如果我们要求必须在O(n)时间内得到结果呢?随机数优化的快速排序变体:随机快速选择能完成这个工作。 

    思路

    在快速选择中,我们不得不进行全部的递归与回溯过程来实现数组的完全排序。

    但是在只要求某个元素位置正确时,我们注意到:

    1. void quick_sort(int arr[], int l,int r) {
    2. if (r-l<=1)return ;
    3. int pos = partition(arr, l, r);
    4. quick_sort(arr, l, pos);
    5. quick_sort(arr, pos + 1, r);
    6. }

    两个子区间排序的其中一个是不必要的,并且如果已经安放了正确的元素位置,以后的所有递归都是不必要的。

    算法过程 

    那么快速选择过程就可以进行对应的优化。

    这就意味着:

    ①我们只需要判断parition分区函数返回的pos与期望位置之间的关系,并转发给对应的子排序

    ②当got_ans==true时,我们可以直接返回来实现剪枝。

    1. void quick_select(vector<int>& nums,int l,int r,int& k,const int& len,bool& got_ans){
    2. if(r-l<1||got_ans)return ;
    3. int pos=partition(nums,l,r);
    4. if(pos==len-k){got_ans=true;return;}
    5. if(pos>len-k)quick_select(nums,l,pos,k,len,got_ans);
    6. if(posquick_select(nums,pos+1,r,k,len,got_ans);
    7. }

    partition函数仍然保持原状:

    1. int partition(vector<int>& nums,int l,int r){
    2. int pivot=l+mt()%(r-l);
    3. swap(nums[pivot],nums[r-1]);
    4. int i,j;
    5. for(i=l,j=l;jif(nums[j]<=nums[r-1])swap(nums[i++],nums[j]);
    6. return i-1;
    7. }

     由于此算法是随机快速排序的特化体,故为随机快速选择。

    复杂度

    时间复杂度:O(n)

    空间复杂度:O(logn)

    复杂度分析: 

    时间分析:

    在理想情况下,每次都转发给了排序范围折半的子函数。

    总用时为T(n),两个T(n/2)为下一级的总时间,n为本次分区所用时间,

    T(n)=T(n/2)+n

              =T(n/4)+n/2+n

              ...

              =T(1)+n(1-1/2^n)/(1-1/2)

              =1+2n+n/2^(n-1)

    省去小量,得到O(n)


    空间分析:

    与快速排序相同,每一级子函数都使用了常量空间,因此空间复杂度是logn级别的。

    Code

    1. class Solution {
    2. private:
    3. mt19937 mt;
    4. public:
    5. int partition(vector<int>& nums,int l,int r){
    6. int pivot=l+mt()%(r-l);
    7. swap(nums[pivot],nums[r-1]);
    8. int i,j;
    9. for(i=l,j=l;jif(nums[j]<=nums[r-1])swap(nums[i++],nums[j]);
    10. return i-1;
    11. }
    12. void quick_select(vector<int>& nums,int l,int r,int& k,const int& len,bool& got_ans){
    13. if(r-l<1||got_ans)return ;
    14. int pos=partition(nums,l,r);
    15. if(pos==len-k){got_ans=true;return;}
    16. if(pos>len-k)quick_select(nums,l,pos,k,len,got_ans);
    17. if(posquick_select(nums,pos+1,r,k,len,got_ans);
    18. }
    19. int findKthLargest(vector<int>& nums, int k) {
    20. bool got_ans=false;
    21. const int len=nums.size();
    22. quick_select(nums,0,len,k,len,got_ans);
    23. return nums[len-k];
    24. }
    25. };
  • 相关阅读:
    袋鼠云数栈UI5.0体验升级背后的故事:可用性原则与交互升级
    进程和线程的区别
    pytest + yaml 框架 - 2.extract 提取结果与接口之间的参数关联
    函数指针数组
    自动化工具
    Java 设置 httponly cookie
    零基础学前端(五)HTML+CSS实战:模仿百度网站首页
    GitHub配置SSH Keys步骤
    不要再把数据可视化搞成表面工程,论数据可视化的正确逻辑
    【JavaWeb】jsp
  • 原文地址:https://blog.csdn.net/dakingffo/article/details/141098237