7.3 分治-快排:LeetCode 215.数组中的第K个最大元素
1. 题目链接
LeetCode 215. 数组中的第K个最大元素
题目要求:给定整数数组 nums
和整数 k
,返回数组中第 k
大的元素。例如,数组 [3,2,1,5,6,4]
中第2大的元素是5。
2. 题目描述
- 输入:整数数组
nums
和整数k
,例如nums = [3,2,1,5,6,4], k = 2
。 - 输出:第
k
大的元素,例如5
。 - 要求:时间复杂度尽量低,且不允许使用内置排序函数。
3. 示例分析
示例 1:
输入:nums = [3,2,1,5,6,4], k = 2
输出:5
分析:
排序后的数组为 [1,2,3,4,5,6]
,倒数第2个元素是5。
示例 2:
输入:nums = [2,2,3,1], k = 2
输出:2
分析:
排序后数组为 [1,2,2,3]
,倒数第2个元素是2。
若使用三路快速排序,重复元素会被集中处理,减少递归次数。
4. 算法思路
核心思想:随机化三路快速排序
- 三路划分:将数组分为三部分:
< key
、== key
、> key
。- 优点:避免重复元素的重复比较,提升效率。
- 随机选择基准值:随机选取
key
防止最坏时间复杂度退化。 - 分治策略:递归处理左右子数组,中间部分(
== key
)无需处理。
求解第K大的元素
- 排序:对数组进行升序排序。
- 直接访问:第
k
大的元素位于排序后的nums[nums.size() - k]
。
5. 边界条件与注意事项
- 输入合法性:
- 数组非空,且
1 ≤ k ≤ nums.size()
(题目保证)。
- 数组非空,且
- 随机化基准值:
- 使用
srand(time(NULL))
初始化随机种子,避免固定划分导致性能下降。
- 使用
- 三路划分终止条件:
- 递归终止条件
if (a >= b) return;
确保子数组长度为1时停止。
- 递归终止条件
- 时间复杂度:
- 平均
O(n log n)
,最坏O(n²)
(概率极低)。
- 平均
- 空间复杂度:
- 递归栈深度平均
O(log n)
,最坏O(n)
。
- 递归栈深度平均
6. 代码实现与逐行解析
class Solution {
public:
int findKthLargest(vector<int>& nums, int k) {
srand(time(NULL)); // 初始化随机种子
qsort(nums, 0, nums.size() - 1); // 三路快速排序
return nums[nums.size() - k]; // 直接访问第k大元素
}
void qsort(vector<int>& nums, int a, int b) {
if (a >= b) return; // 终止条件:子数组长度为0或1
int key = getRandomValue(nums, a, b); // 随机选择基准值
// 三路划分:初始化指针
int i = a, left = a - 1, right = b + 1;
while (i < right) {
if (nums[i] < key) {
swap(nums[++left], nums[i++]); // 小于key,交换到左区
} else if (nums[i] == key) {
i++; // 等于key,跳过
} else {
swap(nums[--right], nums[i]); // 大于key,交换到右区
}
}
// 递归处理左右子数组
qsort(nums, a, left); // 左区:[a, left]
qsort(nums, right, b); // 右区:[right, b]
}
int getRandomValue(vector<int>& nums, int a, int b) {
int r = rand(); // 生成随机数
return nums[a + r % (b - a + 1)]; // 返回区间[a, b]内的随机元素
}
};
代码解析
- 主函数
findKthLargest
:- 初始化随机种子,调用
qsort
排序数组,返回第k
大元素。
- 初始化随机种子,调用
- 三路快速排序
qsort
:- 随机基准值:通过
getRandomValue
从[a, b]
随机选取元素,避免固定选首元素导致最坏情况。 - 三路划分:
left
指向小于key
的右边界,right
指向大于key
的左边界。- 循环结束后,区间划分为:
[a, left] < key
,(left, right) == key
,[right, b] > key
。
- 递归处理:仅对左右子数组递归排序,中间部分已有序。
- 随机基准值:通过
- 随机函数
getRandomValue
:- 生成
[a, b]
内的随机索引,确保基准值选取的随机性。
- 生成
总结
通过三路快速排序结合随机化基准值,能够高效处理重复元素,平均时间复杂度为 O(n log n)
。虽然题目可以进一步优化为快速选择算法(平均 O(n)
),但本实现通过完整排序直观解决问题,且代码简洁易懂。三路划分在处理大量重复数据时优势明显,是快速排序的重要优化方向。