今天来看一到算法题!经典面试题了,将从时间复杂度一般的解法,再到最优解!!!

题目:查找一个无序数组中,第K大的数。LeetCode链接

解法一、堆
分析:既然是求第K大的数,那么很显然,可以使用TOPK问题的角度来解题。只需建一个小根堆,堆的大小就是K,遍历一遍数组:
- 如果此时堆的大小<K,直接往里面放数据。
- 如果此时堆的大小>=K,那么只需比较堆顶与arr[i]的大小,如果堆顶的元素小于arr[i],就弹出堆顶,再放入arr[i]即可。

public int findKthLargest(int[] nums, int k) {
if (nums == null || nums.length == 0 || k <= 0 || k > nums.length) {
return -1;
}
PriorityQueue<Integer> minHeap = new PriorityQueue<>();
int size = 0; //堆的大小
for (int i = 0; i < nums.length; i++) {
if (size < k) {
//堆的大小 < K
size++;
minHeap.add(nums[i]);
} else {
//堆的大小 >= K
if (minHeap.peek() < nums[i]) {
minHeap.poll();
minHeap.add(nums[i]); //放入比较大的元素
}
}
}
return minHeap.poll();
}
以上这种解法,因为整体遍历了一遍数组,是O(N),而遍历的每一个数,插入堆中最坏的情况就是O(logK),整体时间复杂度O(N*logK),空间复杂度O(K)这样的效率,还是达不到面试官的要求。我们接着看下一解法。
解法二、改进“荷兰国旗问题”
我们在之前学快速排序的时候,说到过一个优化,就是荷兰国旗问题优化。当时说的是根据一个基准值,将整个区域划分为大于区、小于区和等于区,此时这里也是一样的。我们既然要找第K大的数,对应在数组中,那就是下标为K-1的数据。
说到这里,我想你可能就理解我的意思了。
那就是,根据划分后的区域,小于区、等于区和大于区,判断K-1,是在这三个区域的哪一块区域内:
- K-1在等于区范围内,那么直接返回等于区的数据即可。
- K-1在大于区范围内,那么递归调用函数,再次进行划分即可。
- K-1在小于区范围内,也是递归调用小于区的范围即可。
如图:

//主方法
public int findKthLargest(int[] nums, int k) {
if (nums == null || nums.length == 0 || k <= 0 || k > nums.length) {
return -1;
}
//因为process函数求的是第K小的数,而题目是要求第K大的数
//所以此时调用的是计算第length-k小的数

本文介绍了三种解决寻找无序数组中第K大元素的算法:堆排序、优化的荷兰国旗问题和bfprt算法。堆排序时间复杂度为O(N*logK),而优化的荷兰国旗问题和bfprt算法则能达到O(N)。bfprt算法通过精心选取基准值,确保了高效性能。
2369





