题目
给定整数数组 nums 和整数 k,请返回数组中第 k 个最大的元素。
请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。
你必须设计并实现时间复杂度为 O(n) 的算法解决此问题。
思路
使用双路快速排序,首先,进行 partition,假设结果是 p。我们只需要将 k 和 p 做比较。
- 如果 k == p,直接返回 arr[p] 即可;
- 如果 k < p,在 arr[l, p - 1] 的范围继续找,即调用 selectK(arr, l, p - 1, k, rnd);
- 如果 k > p,在 arr[p + 1, r] 的范围继续找,即调用 selectK(arr, p + 1, r, k, rnd);
代码
import java.util.Random;
class Solution {
public int findKthLargest(int[] nums, int k) {
Random rnd = new Random();
return selectK(nums, 0, nums.length - 1, nums.length - k, rnd);
}
private int selectK(int[] arr, int l, int r, int k, Random rnd){
int p = partition(arr, l, r, rnd);
if(k == p) return arr[p];
if(k < p) return selectK(arr, l, p - 1, k, rnd);
return selectK(arr, p + 1, r, k, rnd);
}
private int partition(int[] arr, int l, int r, Random rnd){
// 生成 [l, r] 之间的随机索引
int p = l + rnd.nextInt(r - l + 1);
swap(arr, l, p);
// arr[l+1...i-1] <= v; arr[j+1...r] >= v
int i = l + 1, j = r;
while(true){
while(i <= j && arr[i] < arr[l])
i ++;
while(j >= i && arr[j] > arr[l])
j --;
if(i >= j) break;
swap(arr, i, j);
i ++;
j --;
}
swap(arr, l, j);
return j;
}
private void swap(int[] arr, int i, int j){
int t = arr[i];
arr[i] = arr[j];
arr[j] = t;
}
}