【LeetCode】数组中的第K个最大元素 [M](快速排序)

文章介绍了一种基于改进的快速排序算法来寻找数组中第k个最大元素的方法。该算法通过随机选择基准值并应用荷兰国旗问题的排序策略,能在O(n)的时间复杂度内找到答案。在Java代码示例中,算法首先随机选取一个数v,然后对数组进行分区,如果k位于等于v的范围内则返回v,否则调整搜索范围继续查找。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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

一、题目

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

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

你必须设计并实现时间复杂度为 O(n) 的算法解决此问题。

示例 1:

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

示例 2:​​​​​​​

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

提示:

  • 1 <= k <= nums.length <= 105
  • -104 <= nums[i] <= 104

二、代码

class Solution {
    public static int findKthLargest(int[] nums, int k) {
        // 数组长度
        int n = nums.length;
        // 当前这一轮改进后快排的左区间
        int L = 0;
        // 当前这一轮改进后快排的右区间
        int R = n - 1;
        // 当前这一轮改进后快排的左部分的边界,即L~l范围上都是小于v的数,l指向的也是小于v的数
        int l = L - 1;
        // 当前这一轮改进后快排的右部分的边界,即r~R范围上都是大于v的数,r指向的也是大于v的数
        int r = R + 1;
        // 找第k大的数,也就相当于找第n-k+1小的数
        // 但是数组下表是从0开始的,所以这里就是n-k即可
        k = n - k;

        while (true) {
            // 从L~R范围上随机找一个数作为基准
            // 这里先利用随机函数L + Math.random() * (R - L + 1)随机生成下标L~R范围上的数
            // 注意这里要注意在前面加上L,不然求出来的就是随机0~R-L了,我们需要随机L~R
            int v = nums[L + (int) (Math.random() * (R - L + 1))];

            // 下面的流程就是荷兰国旗问题了,将数组中小于v的放左边,等于v的放中间,大于v的放右边
            // 然后用l和r来标记三个部分的边界

            // 当前遍历到的位置,从L开始
            int cur = L;
            // 开始讲数组进行移动
            while (l < r && cur < r) {
                // 小于v的放左边
                if (nums[cur] < v) {
                    // cur放到++l位置
                    swap(++l, cur, nums);
                // 大于v的放右边
                } else if (nums[cur] > v) {
                    // cur放到--r位置
                    swap(--r, cur, nums);
                    // 因为交换到cur位置的数是在右侧,还没有判断过他,所以交换到cur后还是需要在判断一次cur这个新数的大小,所以这里不执行cur++
                    continue;
                }
                // 继续向右遍历判断
                cur++;
            }

            // 至此,小于v的都在小于等于l的位置  l位置是小于v的数
            // 等于v的范围就是大于l小于r
            // 大于v的都在大于等于r的位置   r位置是大于v的数
            if (k > l && k < r) {
                // 如果k落在等于v范围上,直接返回v,v就第k小的数
                return v;
            // 如果此时k落在左部分,那么我们再去左部分去进行荷兰国旗操作
            } else if (k <= l) {
                // 新的范围就是L~l
                // 更新R
                R = l;
                // 在新的范围上初始化r和l
                r = l + 1;
                l = L - 1;
            // 如果此时k落在右部分,那么我们再去右部分去进行荷兰国旗操作
            } else {
                // 新的范围就是r~R
                // 更新L
                L = r;
                // 在新的范围上初始化r和l
                l = L - 1;
                r = R + 1;
            }
        }
    }

    // 交换操作
    public static void swap(int i, int j, int[] nums) {
        int temp = nums[i];
        nums[i] = nums[j];
        nums[j] = temp;
    }
}

三、解题思路 

改进的快排算法,只选一侧, 复杂度O(N)。

无序数组中随机选一个数,这个数叫V,拿它去做整个数组的荷兰国旗问题。

对于V来来说,<它放左边,等于它放中间,>它的放右边。

如果等于的范围能命中K这个数就停,也就是说K这个数在等于的范围内部。如果没命中,然后根据数据状况对左侧或者右侧再随机选。

一个数做荷兰国旗问题,递归下去,总能有找到第K小数的时候。

如果没命中,要么走左边,要么走右边,它是只走一侧的,所以比快排要快,快排需要两侧都要走。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值