• 剑指Offer专项突破版(76)—— 数组中的第 k 大的数字


    题目

    剑指 Offer II 076. 数组中的第 k 大的数字
    在这里插入图片描述

    思路

    假设有个划分函数divide

    • divide:将num在[l,r]范围内,按照nums[l]进行划分,返回一个数组range,划分为:

      • 所有小于nums[l]的数:移动到nums[l,range[0]-1]
      • 所有等于nums[l]的数:移动到nums[range[0],range[1]]
      • 所有大于nums[l]的数:移动到nums[range[1+1],r]

    我们要找第k大的数,可以转化为求第nums.length - k + 1小的数

    对nums在[0,nums.length-1]范围内做划分,结果为range:

    • 如果k就在range的范围内,即 k >= range[0] && k <= range[1],说明第k小的数就是nums[range[0]]
    • 如果k < range[0],说明第k小的数在前半段,即nums[0,range[0]-1]中,那就在这个范围内去递归处理
    • 如果k > range[0],说明第k小的数在后半段,即nums[range[1]+1,]中,那就在这个范围内去递归处理

    最后看看divide怎么实现:

    由于最终需要将数组nums在[l,r]范围内划分为4个区域,因此需要定义这3个区域的边界:

    • 小于nums[l]的区域[l+1,less]

      • 一开始所有区域的范围都是0,因此less初始化为l
    • 等于nums[l]的区域[less+1,i-1]

      • 等于区域一定紧跟着小于区域,因此左边界为less+1
      • 其右端点为当前遍历的位置的前一个位置,也就是i-1
      • i会初始化为l+1,因此等于区域的初始范围也是0个元素
    • 大于nums[l]的区域[more,r]

      • 因为需要初始化为0个元素,因此more初始化为r+1
    • 最后还剩下一个未定区域:[i,more-1]

    接着从l+1开始遍历每个元素i,如果

    • nums[ i ]等于nums[l] :什么都不用处理,i++
    • nums[ i ]小于nums[l] :将nums[i]和等于区域的第一个数交换,将小于区域的右边界less++

      • 实质是扩充小于区域
      • 交换过来的数等于nums[l],因此i++,继续后面的循环
      • 这样操作后,整个数组依旧维持4个区域的定义
    • nums[ i ]大于于nums[l] :将nums[i]和大于区域的前一个数交换,将大于区域的左边界–

      • 实质是扩充大于区域
      • 此时由于不清楚交换过来的数和nums[l]的关系,因此需要重新进入循环
      • 这样操作后,整个数组依旧维持4个区域的定义

    在这里插入图片描述

    什么时候结束循环呢?

    当i和more相撞的时候,表示数组中所有的数都遍历过了,且都在正确的位置上

    代码

    class Solution {
    
        public int findKthLargest(int[] nums, int k) {
                 k = nums.length - k + 1 -1 ;
                int l = 0;
                int r = nums.length-1;
                while(true) {
                    int[] range = divide(nums,l,r);
                    if(k >= range[0] && k <= range[1]) {
                        return nums[k];
                    }
                    if(k < range[0]) {
                        r = range[0]-1;
                    } else {
                        l = range[1] + 1;
                    }
                }
        }
    
         private int[] divide(int[] nums,int l,int r) {
                if(l == r) {
                    return new int[]{l,l};
                }
                int p = nums[l];
                // 大于[more,r]
                int more = r + 1;
                // 小于[l+1,less]
                // 等于[less+1,i-1]
                int less = l;
                int i = l + 1;
                for(;i<more;){
                    if(nums[i] < p){
                        swap(nums,i,less+1);
                        i++;
                        less++;
                    } else if(nums[i] > p) {
                        swap(nums,i,more-1);
                        more--;
                    } else {
                        i++;
                    }
                }
                // 等于区域 [less+1,i-1]
                swap(nums,l,less);
                return new int[]{less,i-1};
            }
    
    
            private void swap(int[] nums,int a,int b) {
                int temp = nums[a];
                nums[a] = nums[b];
                nums[b] = temp;
            }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
  • 相关阅读:
    Vue中使组件置顶后悬浮
    ViewBinding 与 Kotlin 委托双剑合璧
    ajax请求实现学生信息的增改查
    vue 实现 word 下载的方式
    node.js --- npm模块 以及 项目配置文件
    python 类的属性
    跑步耳机排行榜,目前最好的六款跑步耳机
    Java项目:SSM场地预订管理系统
    【web-攻击验证机制】(3.3)验证机制执行缺陷:故障开放登录机制、多阶段登录机制中的缺陷、不安全的证书存储
    SparkStreaming (六) --------- 优雅关闭
  • 原文地址:https://blog.csdn.net/qq_39383767/article/details/128108993