[hot 快速选择] 215. 数组中的第K个最大元素

54 阅读1分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第12天,点击查看活动详情

每日刷题 2022.10.10

题目

  • 给定整数数组 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 <= 10^5
  • -10^4 <= nums[i] <= 10^4

解题思路

  • 根据题意可知:需要求数组中的第k个最大元素,而不是第k个不同的元素,也就是多个重复相同的数字也会被计算进去。
  • 题目中还要求,需要使用时间复杂度o(n)。那么就想到了使用优先队列priorityQueue,可以先遍历整个数组一遍,将所有的数依次压入到priorityQueue中,最后再依次从中取k次,那么就获得了第k个最大元素。

思考

  • 全部n个数可能有很大,想要直接全部存储下来,不太可能。
  • 优化方法一:因此优先队列中并不需要将所有的数全部存储下来,很多都是废数,我们只需要第k个。当超过k个之后,和队列顶端的元素对比,小于就不需要存入,大于存入。
  • 优化方法二:使用快速选择,因为我们并不是很关注整个数组排序后的顺序是什么样的,而我们更关心的是数组中的第k个元素是谁,第k个之前和之后的都是哪些数都不关心。可以使用快速选择,将小于的放在prviot的前面,大于放在prviot的后面就可以啦

AC代码

var findKthLargest = function(num, k) {
  let nums = [...num];
  let n = nums.length;
  return qselect(0, n - 1, n - k);
  
  function swap (x, y) {
    let t = nums[x];
    nums[x] = nums[y];
    nums[y] = t;
  }
  function qselect(l, r, k) {
    // 左右的区间都已经安排好,才会只剩下一个元素
    // console.log(l, r, nums[k])
    if(l === r) return nums[k];
    // 主要用于边界处理
    let i = l - 1, j = r + 1, x = nums[l];
    while(i < j) {
      // i的左边表示全部小于 基数 的元素
      do i++; while(nums[i] < x);
      // j的右边表示全部大于 基数 的元素
      do j--; while(nums[j] > x);
      // 两个数值之间存在不符合的,需要交换位置
      if(i < j) swap(i, j);
      // console.log('process::', i,j, k)
    }
    // console.log('end:::', k, j)
    // 需要完之间的所有元素,判断 需要接着遍历左边的 还是 右边的部分
    if(k <= j) return qselect(l, j, k);
    else return qselect(j + 1, r, k);
  }
};