Find the kth largest element in an unsorted array. Note that it is the kth largest element in the sorted order, not the kth distinct element.
For example,
Given [3,2,1,5,6,4]
and k = 2, return 5.
Note:
You may assume k is always valid, 1 ≤ k ≤ array's length.
思路:
- 选取数组合适的element作为pivot (提高效率)
- 使用partition,elements<=pivot的放到pivot前,elements>=pivot的elements放到pivot后,pivot就放到了正确位置position。
- 缩小下次partition查找的范围。如果pivot的position比target大,那么扔掉pivot之后的elements,继续在pivot之前的elements里面找。反之,pivot的position小于target,扔掉pivot之前的elements,再pivot之后的elements里继续partition。这样不断缩小,一直到不能再缩小。
- nums[target] 就是 the kth distinct element。
1ms Java solution beats 99.1%.
public int findKthLargest(int[] nums, int k) {
int n = nums.length, target = n - k;
quicksort(nums, 0, n - 1, target);
return nums[n - k]; //nums[n - k]就是the kth largest element
}
private void quicksort(int[] nums, int start, int end, int target) {
if (start >= end) return;
int mid = start + (end - start) / 2;
int pivot = choosePivot(nums[mid], nums[start], nums[end]);
int i = start, j = end;
while (i <= j) {
while (nums[i] < pivot) i++;
while (nums[j] > pivot) j--;
if (i <= j) {
if (nums[i] != nums[j]) swap(nums, i, j);
i++;
j--;
}
}
if (target <= i - 1) quicksort(nums, start, i - 1, target);
else quicksort(nums, i, end, target);
}
/**选取a,b,c三者的中位数*/
private int choosePivot(int a, int b, int c) {
int min = Math.min(a, Math.min(b, c));
int max = Math.max(a, Math.max(b, c));
return a - max + b - min + c;
}
private void swap(int[] nums, int i, int j) {
int tmp = nums[i];
nums[i] = nums[j];
nums[j] = tmp;
}
思路:
使用优先队列,维护size为k的小顶堆,堆顶的元素就是第k大的元素,而其余元素都比这个元素大。
9ms Java solution beats 66.46%.
public int findKthLargest2(int[] nums, int k) {
PriorityQueue<Integer> pq = new PriorityQueue<Integer>(k);
for (int i = 0; i < nums.length; i++) {
if (pq.size() < k) pq.offer(nums[i]);
else if (nums[i] > pq.peek()) {
pq.poll();
pq.offer(nums[i]);
}
}
return pq.peek();
}
总结:一般快排的话,选取pivot的时候,不能每次只选第一个或者最后一个,可以选第一个或中间的或最后一个,最好选三者中排名第二大的;或者选取pivot之前,将无序数组进行shuffle也是不错的办法,但是效果还是前者更好。