解题思路:
可以直接用二叉堆过滤数组。
public int findKthLargest(int[] nums, int k) {
//最小二叉堆,最小元素再上面
PriorityQueue<Integer> pq = new PriorityQueue<>();
for(int i : nums){
//过滤每个元素
pq.offer(i);
//只要堆大于k,就弹出元素,最后过滤完剩下的顶部元素就是第K大的元素。
if(pq.size() > k){
pq.poll();
}
}
return pq.peek();
}
时间复杂度:O(NlogK)
下面是一种优化方法,利用随机选择排序方法,时间复杂度为O(n)
public int findKthLargest(int[] nums, int k) {
int low = 0, high = nums.length - 1;
int target = nums.length - k; //改变索引
while(true){
int i = partition(nums, low, high);
if(target == i){
return nums[target];
}else if(i < target){
low = i + 1;
}else{
high = i - 1;
}
}
}
private int partition(int[] arr, int low, int high) {
//保持序列随机性,避免时间复杂度最坏情况n^2
if(high > low){
int random = low + new Random().nextInt(high - low);
swap(arr, high, random);
}
/**
* 分区函数,将 arr[high] 作为 pivot 分区点
* i、j 两个指针,i 作为标记“已处理区间”和“未处理区间”的分界点,也即 i 左边的(low~i-1)都是“已处理区”。
* j 指针遍历数组,当 arr[j] 小于 pivot 时,就把 arr[j] 放到“已处理区间”的尾部,也即是 arr[i] 所在位置
* 因此 swap(arr, i, j) 然后 i 指针后移,i++
* 直到 j 遍历到数组末尾 arr[high],将 arr[i] 和 arr[high](pivot点) 进行交换,返回下标 i,就是分区点的下标。
*/
int i = low;
int pivot = arr[high]; //arr[high]处在随机位置
for (int j= low; j < high; j++){
if(arr[j] < pivot){
swap(arr,i, j);
i++;
}
}
swap(arr, i, high);
return i;
}
void swap(int[] nums, int i, int j) {
int temp = nums[i];
nums[i] = nums[j];
nums[j] = temp;
}