题目描述
给定整数数组 nums 和整数 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个最大元素。
Java中可以使用优先级队列实现小根堆,定义该队列的大小为K,首先向其中加入K个元素,后续判断判断当前元素,是否大于堆顶值,如果大于,则移除堆顶,将当前值入堆,最后返回堆顶元素即可。
public int findKthLargest(int[] nums, int k) {
PriorityQueue<Integer> heap = new PriorityQueue<Integer>(k);
for(int i = 0;i < k;i++){
heap.offer(nums[i]);
}
for(int i = k; i < nums.length; i++){
if(nums[i] > heap.peek()){
heap.poll();
heap.offer(nums[i]);
}
}
return heap.peek();
}
我们也可以建立一个大根堆,做 k − 1 次删除操作后堆顶元素就是我们要找的答案。在很多语言中,都有优先队列或者堆的的容器可以直接使用,但是在面试中,面试官更倾向于让更面试者自己实现一个堆。所以建议读者掌握这里大根堆的实现方法,在这道题中尤其要搞懂「建堆」、「调整」和「删除」的过程。
class Solution {
Random rand=new Random();
public int findKthLargest(int[] nums, int k) {
return quickSort(nums,k,0,nums.length-1);
}
private int quickSort(int[] nums,int k,int left,int right){
int index=rand.nextInt(right-left+1)+left;
int flag=nums[index];
nums[index]=nums[left];
int i=left,j=right;
while (i<j){
while (i<j&&nums[j]<=flag) j--;
nums[i]=nums[j];
while (i<j&&nums[i]>=flag) i++;
nums[j]=nums[i];
}
nums[i]=flag;
if (i==k-1) return nums[i];
else if (i<k-1) return quickSort(nums,k,i+1,right);
else return quickSort(nums,k,left,i-1);
}
}
:::caution 避免退化为链表 快速排序的性能和「划分」出的子数组的长度密切相关。所以我们一定要引入随机化,避免极端用例对算法复杂性的影响。
若无脑选择从nums[0]开始划分,那么假设原数组为 [5,4,3,2,1],使用快排查找第5大元素时,算法速度将提升为O(n). :::
TOP-K类题目思路
TOP-K问题,基本均可以通过堆和快排的思想来快速解决,需要注意的是,当使用堆解决时有时面试官可能更希望你自己实现一个堆,所以可以考虑直接拿出快排的方法来解决此问题。