这是一道让我收获很大的题,解题思路是使用quick select排序。首先选出一个pivot(为了方便,把数组的最后一个元素nums[high] 设置成pivot), 然后扫描整个数组。使用了两个指针: i and j,[low, i) 是小于pivot的元素,[i, j) 是大于pivot的元素,[j, high)是未知探索区域。当扫描完整个数组后,将 nums[i] 和 nums[high] 交换位置,这样 index为 i 的元素即 pivot 在整个数组的位置已经固定下来(左边的元素比他小,右边的元素比他大),我们数一下pivot从大到小排在第几位记为 count。如果count = k, 则代表我们找到了想要的元素; count > k说明 pivot 的值小了,我们需要在它的右边继续找 kth largest element; 如果count < k, 意味着pivot的值大了,我们需要在他的左手边找到 (k - count)th largest element, 那么这个元素就是整个数组里 kth largest element。代码如下:
class Solution {
public int findKthLargest(int[] nums, int k) {
return quickSelect(nums, 0, nums.length - 1, k);
}
private int quickSelect(int[] nums, int low, int high, int k){
int i = low;
// Here we pick nums[high] as the pivot
for(int j = low; j < high; j++){
if(nums[j] <= nums[high]){
swap(nums, i++, j);
}
}
swap(nums, i, high);
int count = high - i + 1;
if(count == k){
return nums[i];
}else if(count > k){
return quickSelect(nums, i + 1, high, k);
}else{
return quickSelect(nums, 0, i - 1, k - count);
}
}
private void swap(int[] arr, int left, int right){
int temp = arr[right];
arr[right] = arr[left];
arr[left] = temp;
}
}
最近学习了heap以后,又掌握了另一种方法:创建一个大小为k的min heap, 其物理意义就是我们当前找到的最大的k个数。先把数组的前k个数放进去,然后继续遍历后面的数,每次都和堆顶的数进行比较,如果比堆顶的数要大,就把堆顶的数pop()出去,再offer()当前的数。当这一个过程完成以后,堆顶的数就是我们要的 kth largest element。还有一个总结就是,关于比较器的lambda表达式,你需要脑补一个<0在后面。例如(a,b) -> a - b,可以理解成当 a - b < 0即 a < b 时,a排在b的前面。因此这是一个最小堆。代码如下:
class Solution {
public int findKthLargest(int[] nums, int k) {
PriorityQueue<Integer> minHeap = new PriorityQueue<>(k, (a,b) -> a - b);
for(int i = 0; i < k; i++){
minHeap.offer(nums[i]);
}
for(int i = k; i < nums.length; i++){
int temp = minHeap.peek();
if(nums[i] > temp){
minHeap.poll();
minHeap.offer(nums[i]);
}
}
return minHeap.peek();
}
}