题目描述
给定整数数组 nums 和整数 k,请返回数组中第 **k** 个最大的元素。
请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。
你必须设计并实现时间复杂度为 O(n) 的算法解决此问题。
解题思路
- 快速选择算法:
- 随机化分区:选取随机pivot将数组分为大于/小于pivot的两部分
- 分治策略:根据pivot位置与k的关系,递归处理左或右子数组
- 终止条件:pivot正好是第k大元素时返回
- 堆排序优化:
- 构建大小为k的最小堆
- 遍历数组,维护堆顶为当前第k大元素
- 最终堆顶即为结果
关键洞察
- 快速选择本质:快速排序的变种,但只需处理目标所在分区
- 随机化避免最坏:随机选择pivot防止有序数组的O(n²)退化
- 堆的适用场景:海量数据流场景(空间效率高)
- 索引转换:第k大元素下标 =
n - k(升序排序)
复杂度分析
| 方法 | 时间复杂度 | 空间复杂度 |
|---|---|---|
| 快速选择 | 平均O(n),最坏O(n²) | 递归栈O(log n) |
| 堆方法 | O(n log k) | O(k) |
| 排序法 | O(n log n) | O(1) |
最优选择:
- 常规场景:快速选择(平均线性时间)
- 内存受限:堆方法(仅需O(k)空间)
- 巨量数据流:堆方法是唯一可行解
代码实现
/**
* @link {https://leetcode.cn/problems/kth-largest-element-in-an-array}
* @param {number[]} nums
* @param {number} k
* @return {number}
*/
var findKthLargest = function (nums, k) {
const quickSelect = (num, l, r, k) => {
if (l === r) return num[k];
const x = num[l];
let i = l - 1, j = r + 1;
while (i < j) {
do i++; while (num[i] < x);
do j--; while (num[j] > x);
if (i < j) {
[num[i], num[j]] = [num[j], num[i]]
}
}
if (k <= j) {
return quickSelect(num, l, j, k);
} else {
return quickSelect(num, j + 1, r, k);
}
}
const n = nums.length;
return quickSelect(nums, 0, n - 1, n - k);
};