c++每日一题-数组当中第K个最大的元素

c++每日一题-数组当中第K个最大的元素

一、题目描述

给定整数数组 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

分析

这道题可以使用 快速排序的思想 来做。
如果我们直接把整个数组排成降序,然后返回 nums[k - 1],时间复杂度是:O(n log n)
因此可以使用 快速选择 Quick Select。

快速选择和快速排序非常像:
快速排序:每次 partition 后,左右两边都继续排序
快速选择:每次 partition 后,只处理目标下标所在的一边

如果数组按降序排列,那么第 k 大元素的位置就是:int target = k - 1;
每次随机选择一个基准值 p,然后把数组划分成三部分(三路划分 ):

//大于 p 的区域 | 等于 p 的区域 | 小于 p 的区域
[left, l]      > p //如果 target <= l,说明答案在左边大于 p 的区域
[l + 1, r - 1] == p //否则,说明 target 落在等于 p 的区域,直接返回 p
[r, right]     < p //如果 target >= r,说明答案在右边小于 p 的区域

具体代码相交于普通的快排有两个优化:

  • 使用随机数来获得基准元素
  • 三路划分优化:按照基准元素,将所有元素分成三个区间。左部分全部⼩于 pivot,中间部分全部等于 pivot,右部分全部⼤于 pivot。然后中间部分就不⽤管了,直接递归处理左右部分。
#include <vector>
#include <algorithm>
#include <cstdlib>
#include <ctime>
using namespace std;

class Solution {
public:
    int findKthLargest(vector<int>& nums, int k) {
        srand(time(0));

        int target = k - 1; // 第 k 大元素在降序数组中的位置
        int left = 0;
        int right = nums.size() - 1;

        while (left <= right) {
            int l = left - 1;
            int i = left;
            int r = right + 1;

            // 随机选择一个基准值
            int p = get_random(nums, left, right);

            // 三路划分:
            // [left, l]      > p
            // [l + 1, r - 1] == p
            // [r, right]     < p
            while (i < r) {
                if (nums[i] > p) {
                    swap(nums[i++], nums[++l]);
                } else if (nums[i] == p) {
                    i++;
                } else {
                    swap(nums[--r], nums[i]);
                }
            }

            if (target <= l) {
                right = l;
            } else if (target >= r) {
                left = r;
            } else {
                return p;
            }
        }

        return -1;
    }

    int get_random(vector<int>& nums, int left, int right) {
        return nums[rand() % (right - left + 1) + left];
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值