数组中的第K个最大元素
LeetCode:数组中的第K个最大元素
题目描述:
在未排序的数组中找到第 k 个最大的元素。请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。
示例:
输入: [3,2,3,1,2,4,5,5,6] 和 k = 4
输出: 4
思想:
方法一:优先队列:可直接使用PriorityQueue建立小根堆。遍历数组,所有元素入队,保证Queue中只有k个元素,超过k就出队,这样最后Queue中剩余的k个元素一定前k个最大元素。时间复杂度O(nlog2k),空间复杂度O(k)
方法二:快速选择:基于快速排序的方法。这里与快排的区别在于,partition取得枢轴之后,左子表或者右子表,选一个继续操作即可,快排是左右子表都要继续递归。
注意:由于本题存在极端样例,导致快排耗时依然很高(因为快排是越混乱,速度相对越快)。这里用到一个小技巧,partition方法中选择初始枢轴时,不直接选择low指针对应的元素,而是用随机数取一个下标
if(high>low){
int idx = (int)(low+1+Math.random()*(high-low));
arr[idx]^=arr[low];
arr[low]^=arr[idx];
arr[idx]^=arr[low];
}
加了这一步之后,耗时击败99%。时间复杂度O(n),空间复杂度O(1)
代码:
方法一:优先队列
public int findKthLargest(int[] nums, int k) {
PriorityQueue<Integer> queue = new PriorityQueue<>();
for(int item : nums){
queue.offer(item);
if(queue.size()>k) queue.poll();
}
return queue.peek();
}
方法二:快速选择
public int findKthLargest(int[] nums, int k) {
int low = 0;
int high = nums.length-1;
k = nums.length-k;
while(low<high){
int i = partition(nums,low,high);
if(i==k) return nums[i];
if(i>k) high = i - 1;
else low = i + 1;
}
return nums[k];
}
private int partition(int[] arr, int low, int high){
//这一步炒鸡关键
if(high>low){
int idx = (int)(low+1+Math.random()*(high-low));
arr[idx]^=arr[low];
arr[low]^=arr[idx];
arr[idx]^=arr[low];
}
int pivotKey = arr[low];
while(low<high){
while(low<high&&arr[high]>=pivotKey) --high;
arr[low] = arr[high];
while(low<high&&arr[low]<=pivotKey) ++low;
arr[high] = arr[low];
}
arr[low] = pivotKey;
return low;
}