链接:
https://leetcode.com/problems/kth-largest-element-in-an-array/
大意:
给定一个无序且有重复数字的整型数组nums,要求找出第k大的数(k为有效数字,1 <= k <= nums.length)。例子:
思路:
基于堆的思想。维护一个大小始终为k的小根堆,当当前元素num大于堆顶元素时,将堆顶元素出堆,并将当前元素加入堆中(同时调整堆)。
可以使用Java中自带的以堆思想实现的数据结构优先级队列(PriorityQueue)来解决 (不用重复造轮子了。。)
代码:
class Solution {
public int findKthLargest(int[] nums, int k) {
PriorityQueue<Integer> pq = new PriorityQueue<>();//小根堆
for (int num : nums) {
if (pq.size() < k)
pq.offer(num);
else {
int val = pq.peek(); // 获得堆顶元素
if (num > val) {
pq.poll();
pq.offer(num);
}
}
}
return pq.peek();
}
}
结果:
结论:
除了可以使用堆排序思想来解决,另一个高效方法是通过快排思想但并不对所有元素进行排序,只为找到第k个位置上的数。
改进:(自己写了个类似快排,效率高的原因得归功于基准的随机选取!!!)
class Solution {
public int findKthLargest(int[] nums, int k) {
return myQuickSort(nums, k, 0, nums.length - 1);
}
public int myQuickSort(int[] nums, int k, int s, int e) {
// 随机选择基准
Random r = new Random();
int index = r.nextInt(e - s + 1) + s; // 随机选择基准后,效率大大提高
swap(nums, index, s); // 将首元素与基准交换
int axes = nums[s]; // 基准
int start = s + 1, end = e;
// 比axes大或等于的元素在axes的左边 比axes小的元素在axes右边
while (start <= end) {
// 找到第一个比axes小的元素
while (start <= end && nums[start] >= axes) {
start++;
}
// 找到第一个比axes大或等于的元素
while (end >= start && nums[end] < axes) {
end--;
}
// axes为数组的第end+1大元素 end为axes在nums中的位置
if (start > end) {
if (end + 1 == k)
return axes;
swap(nums, s, end);
if (end + 1 > k)
return myQuickSort(nums, k, s, end - 1);
if (end + 1 < k)
return myQuickSort(nums, k, end + 1, e);
}
swap(nums, start++, end--);
}
if (end + 1 == k)
return axes;
swap(nums, s, end);
if (end + 1 > k)
return myQuickSort(nums, k, s, end - 1);
else
return myQuickSort(nums, k, end + 1, e);
}
public void swap(int[] nums, int idx1, int idx2) {
int tmp = nums[idx1];
nums[idx1] = nums[idx2];
nums[idx2] = tmp;
}
}