寻找第 K 大的数
1、参考资料
https://leetcode-cn.com/problems/kth-largest-element-in-an-array/
https://www.jianshu.com/p/33ee33ce8699
2、题目要求
题目描述
在未排序的数组中找到第 k 个最大的元素。请注意,你需要找的是数组排序后的第 k
个最大的元素,而不是第 k
个不同的元素。
示例 1:
输入: [3,2,1,5,6,4] 和 k = 2
输出: 5
示例 2:
输入: [3,2,3,1,2,4,5,5,6] 和 k = 4
输出: 4
说明:
你可以假设 k
总是有效的,且 1 ≤ k ≤ 数组的长度。
3、代码思路
缩减版的快排
- 刚开始肯定会想到使用快排,先对
nums
数组进行快速排序,然后再返回第k
大的数:nums[index]
,其中index
为倒数第k
个元素的下标:index=nums.length-k
- 仔细想想,因为我们只需要求取第
k
的数,其实完全不用对整个数组进行快排,可以使用阉割版的快排 - 我们调用
partition = int partition(int[] arr, int left, int right) {
函数将数组分为左右两半,其实就已经可以排除掉一部分元素,将不需要进行排序的数组区间剔除
剔除无需排序的区间:我们需要判断 partition
与 index
的关系
- 调用
partition()
函数后,在partition
左边的元素都小于等于arr[partition]
,在partition
右边的元素都大于等于arr[partition]
。 - 如果
partition = index
,恭喜,运气不错,说明第k
大的元素恰好就在partition
处,已经找到第k
大的元素,结束递归 - 如果
partition > index
,则说明第k
大的元素在[partition+1, right]
之间,对该区间进行递归快排 - 如果
partition < index
,则说明第k
大的元素在[left, partition-1]
之间,对该区间进行递归快排
时间复杂度近似为 O(n)
4、代码实现
代码:寻找第 K
大的数
class Solution {
public int findKthLargest(int[] nums, int k) {
quickSortK(nums, 0, nums.length - 1, nums.length - k);
return nums[nums.length - k];
}
private void quickSortK(int[] arr, int left, int right, int index) {
// 如果 left >= right,则证明此条路径的递归快排完成
if (left < right) {
// 将数组分为左右两半,partition 就是中间的分界线
int partition = partition(arr, left, right);
if (partition == index) {
// 如果 partition == index,则说明 partition 就是第 k 大的元素
return;
} else if (partition > index) {
// 如果 partition > index,则说明第 k 大的元素在区间左边,在左半区间递归进行快排
quickSortK(arr, left, partition - 1, index);
} else {
// 如果 partition < index,则说明第 k 大的元素在区间右边,在右半区间递归进行快排
quickSortK(arr, partition + 1, right, index);
}
}
}
// 将数组分为左右两半
private int partition(int[] arr, int left, int right) {
int pivot = arr[left];
//终止while循环以后left和right一定相等的
while (left < right) {
while (left < right && arr[right] >= pivot) {
--right;
}
arr[left] = arr[right];
while (left < right && arr[left] <= pivot) {
++left;
}
arr[right] = arr[left];
}
arr[left] = pivot;
//left可以改为right
return left;
}
}
下面是快排的代码,来和原版的快排对比一下
private static void quickSort(int[] arr, int left, int right) {
if (left < right) {
int partitionIndex = partition(arr, left, right);
quickSort(arr, left, partitionIndex - 1);
quickSort(arr, partitionIndex + 1, right);
}
}
private static int partition(int[] arr, int left, int right) {
int pivot = arr[left];
//终止while循环以后left和right一定相等的
while (left < right) {
while (left < right && arr[right] >= pivot) {
--right;
}
arr[left] = arr[right];
while (left < right && arr[left] <= pivot) {
++left;
}
arr[right] = arr[left];
}
arr[left] = pivot;
//right可以改为left
return left;
}