题目
给定整数数组 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
代码&注释
java
class Solution {
/**
* 查找数组中第 k 个最大的元素(即第 k 大)
*
* 思路:先对整个数组进行升序排序,然后第 k 大就是倒数第 k 个元素,
* 即索引为 nums.length - k 的元素。
*
* 注意:此方法时间复杂度为 O(n log n),不是最优解(最优可用快速选择,平均 O(n)),
* 但逻辑简单、易于理解。
*
* @param nums 输入数组
* @param k 第 k 大(k 从 1 开始计数)
* @return 第 k 个最大的元素;若输入非法,返回 0
*/
public int findKthLargest(int[] nums, int k) {
// 边界检查:数组为空、长度为 0 或 k 超出范围
if (nums == null || nums.length == 0 || k > nums.length) {
return 0;
}
// 对整个数组进行升序快速排序
quick_sort(nums, 0, nums.length - 1);
// 排序后,第 k 大的元素位于倒数第 k 个位置
// 例如:[1,2,3,4,5],第 2 大是 4,索引 = 5 - 2 = 3
return nums[nums.length - k];
}
/**
* 快速排序(递归实现,原地排序)
*
* 采用"三路划分"思想的简化版(实际是标准双指针分区)
* 目标:将数组 [l, r] 区间按升序排列
*
* @param nums 待排序数组
* @param l 左边界(包含)
* @param r 右边界(包含)
*/
public void quick_sort(int[] nums, int l, int r) {
// 递归终止条件:区间无效(只有一个元素或空)
if (l >= r) {
return;
}
// 初始化左右指针
int i = l; // 左指针,从左向右扫描
int j = r; // 右指针,从右向左扫描
// 选择基准值(pivot):取中间位置的元素,避免最坏情况(如已排序数组)
// 使用 l + (r - l) / 2 防止整数溢出(等价于 (l + r) / 2,但更安全)
int pivot = nums[l + (r - l) / 2];
// 分区过程:将小于 pivot 的放左边,大于 pivot 的放右边
while (i <= j) {
// 从左边找到第一个 >= pivot 的元素
while (nums[i] < pivot) {
i++;
}
// 从右边找到第一个 <= pivot 的元素
while (nums[j] > pivot) {
j--;
}
// 如果两个指针未交错,说明找到了一对需要交换的元素
if (i <= j) {
// 交换 nums[i] 和 nums[j]
int temp = nums[i];
nums[i] = nums[j];
nums[j] = temp;
// 移动指针,继续扫描
i++;
j--;
}
}
// 递归处理左右两个子区间
// 注意:此时 j 是左半部分的右边界,i 是右半部分的左边界
if (l < j) {
quick_sort(nums, l, j); // 排序左半部分 [l, j]
}
if (i < r) {
quick_sort(nums, i, r); // 排序右半部分 [i, r]
}
}
}