【算法详解】数组中的第K个最大元素(力扣215.数组中的第K个最大元素)

一、题目描述

力扣链接:力扣215.数组中的第K个最大元素

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

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

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

二、C++题解

class Solution {
public:
    int findKthLargest(vector<int>& nums, int k) {
        sort(nums.begin(), nums.end(), [&](int a, int b){return a > b;});
        return nums[k-1];
    }
};

如果可以用sort(),很好解决,但是复杂度为O(NlogN),而题目要求时间复杂度为O(N)。

采用快速选择的方法:

class Solution {
public:
    int findKthLargest(vector<int>& nums, int k) {
        return quickSlect(nums, k);
    }

private:
    int quickSlect(vector<int>& nums, int k)  {
        vector<int> small, equal, large;
        // 随机寻找一个点作为分割点
        int pivot = nums[rand() % nums.size()];
        for (auto num : nums) {
            if (num < pivot) small.push_back(num);
            else if (num > pivot) large.push_back(num);
            else equal.push_back(num);
        }

        // 如果目标点在large里
        if (k <= large.size()) {
            return quickSlect(large, k);
        }
        
        // 如果目标点在small里
        if (k > large.size() + equal.size()) {
            return quickSlect(small, k - (large.size() + equal.size()));
        }

        // 第 k 大元素在 equal 中,直接返回 pivot
        return pivot;
    }

};

三、思路解析

快速排序的核心包括“哨兵划分” 和 “递归” 。

  • 哨兵划分: 以数组某个元素(一般选取首元素)为基准数,将所有小于基准数的元素移动至其左边,大于基准数的元素移动至其右边。
  • 递归: 对 左子数组右子数组 递归执行 哨兵划分,直至子数组长度为 1 时终止递归,即可完成对整个数组的排序。

然而,对于包含大量重复元素的数组,每轮的哨兵划分都可能将数组划分为长度为 1 和 n−1 的两个部分,这种情况下快速排序的时间复杂度会退化至 O ( N 2 ) O(N^2) O(N2)

一种解决方案是使用「三路划分」,即每轮将数组划分为三个部分:小于等于大于基准数的所有元素。这样当发现第 k 大数字处在“等于基准数”的子数组中时,便可以直接返回该元素。

为了进一步提升算法的稳健性,我们采用随机选择的方式来选定基准数:int pivot = nums[rand() % nums.size()];

  • 9
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值