一、题目描述
1.1 题目
-
数组中的第K个最大元素
-
在未排序的数组中找到第 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 ≤ 数组的长度。
1.2 知识点
- 堆
- 快速选择
- 快速排序算法
1.3 题目链接
二、解题思路
2.1 自研思路
这道题我采用的是 快速选择 ,该算法的思路是建立在快速排序算法之上,首先分析题目要求,我们可以发现其实我们并不要数组整体有序,只需保证前 K 个元素有序(包括第 K 个元素),就可以求出数组中的第 K 大元素,因此我们有两种思路:
-
使用堆排序,维护一个大小为 K 的 大顶堆 ,保证堆顶元素永远是该堆中的第 K 大元素,然后遍历数组并将元素不断插入到该大顶堆即可,当遍历完成后,该大顶推堆顶元素即为所求;
-
使用选择算法,该算法的核心思想是利用快速排序算法每次确定一个元素的位置,并且可以确定的是该元素的在数组中左侧的元素均小于该元素,该元素在数组中右侧的元素均大于该元素,但需要注意的是当前左右侧元素虽然大小关系确定,但是是无序的。所以我们确定该元素的下标后,判断该下标与我们要寻找的 K 大元素的大小关系,经过判断后就可以将下一轮搜索的范围缩小一半,比如当 nums.length-k(数组从小到大排列,第 k 大元素下标为 nums.length-k)小于当前元素的下标时,则可以确定我们需要找的排序后下标为 nums.length-k 的元素应该位于当前下标的右侧,所以我们缩小搜索范围为 start ~ index-1 ;
2.2 相关知识点
三、实现代码
3.1 自研实现
class Solution {
public int findKthLargest(int[] nums, int k) {
return nums[findKthLargest(nums, 0, nums.length-1, nums.length-k)];
}
private int findKthLargest(int[] nums, int start, int end, int k) {
int minPartitionEnd = partition(nums, start, end);
if(minPartitionEnd == k)
return minPartitionEnd;
else if(minPartitionEnd > k)
return findKthLargest(nums, start, minPartitionEnd-1, k);
else
return findKthLargest(nums, minPartitionEnd+1, end, k);
}
private int partition(int[] nums, int start, int end) {
int target = nums[end], minPartitionEnd = start-1;
for(int i = start; i < end; i++)
if(nums[i] <= target) swap(nums, ++minPartitionEnd, i);
swap(nums, ++minPartitionEnd, end);
return minPartitionEnd;
}
private void swap(int[] arr, int a, int b){
int temp = arr[a];
arr[a] = arr[b];
arr[b] = temp;
}
}
3.2 示例代码(堆)
class Solution {
public int findKthLargest(int[] nums, int k) {
// init heap 'the smallest element first'
PriorityQueue<Integer> heap =
new PriorityQueue<Integer>((n1, n2) -> n1 - n2);
// keep k largest elements in the heap
for (int n: nums) {
heap.add(n);
if (heap.size() > k)
heap.poll();
}
// output
return heap.poll();
}
}