【经典算法题】第K大数
Leetcode 0215 数组中的第K个最大元素
分析
- 本题的考点:快排、堆。
快排
-
首先因为
k
是从1开始的,但是数组从下标0开始,因此第1大的数据在排序后的数组中的nums[0]
的位置,所以我们要将k--
。 -
每次快排会根据分界点
x
的值将数据分为两部分,即[l, j]、[j + 1, r]
(这种情况x
不能取右边界,否则死循环)或者[l, i - 1]、[i, r]
这种情况x
不能取左边界,否则死循环)。 -
如果使用第一种区间分割方式,即
[l, j]、[j + 1, r]
,然后根据j
和k
的大小关系,判断应该到哪边递归查找,如果k<=j
则应该到左边查找,否则应该到右边查找。 -
快排使用
C++
演示。
堆
-
另外这一题还可以使用堆解决,这里需要使用小顶堆,堆中存储
k
个数据,依次遍历数组中的元素x
,当堆中不足k
个元素时,直接将x
加入堆中;当堆中元素为k
个时,并且x
大于堆顶元素时,将堆顶元素弹出,插入x
,若x
小于等于堆顶元素时,直接遍历下一个元素即可。 -
用
Java
演示这种方法。
代码
- C++
// 分为区间:[l, j]、[j + 1, r],注意x不能取到右端点
class Solution {
public:
int quick_sort(vector<int> &q, int l, int r, int k) {
if (l == r) return q[k]; // 此时l, r, k相等
int x = q[l + r >> 1], i = l - 1, j = r + 1;
while (i < j) {
while (q[++i] > x) ;
while (q[--j] < x) ;
if (i < j) swap(q[i], q[j]);
}
if (k <= j) return quick_sort(q, l, j, k);
else return quick_sort(q, j + 1, r, k);
}
int findKthLargest(vector<int>& nums, int k) {
return quick_sort(nums, 0, nums.size() - 1, k - 1); // 注意这里是k-1
}
};
/* 分为区间:[l, i - 1]、[i, r],注意x不能取到左端点
class Solution {
public:
int quick_sort(vector<int> &q, int l, int r, int k) {
if (l == r) return q[k]; // 此时l, r, k相等
int x = q[l + r + 1 >> 1], i = l - 1, j = r + 1;
while (i < j) {
do i++; while (q[i] > x);
do j--; while (q[j] < x);
if (i < j) swap(q[i], q[j]);
}
if (k <= i - 1) return quick_sort(q, l, i - 1, k);
else return quick_sort(q, i, r, k);
}
int findKthLargest(vector<int>& nums, int k) {
return quick_sort(nums, 0, nums.size() - 1, k - 1);
}
};
*/
- Java
class Solution {
public int findKthLargest(int[] nums, int k) {
Queue<Integer> q = new PriorityQueue<>();
for (int x : nums) {
q.add(x);
if (q.size() > k) q.remove();
}
return q.peek();
}
}
时空复杂度分析
-
时间复杂度:快排: O ( n ) O(n) O(n),
n
为数组长度,因为每次只会选择一边递归,所以计算量大约为: n + n 2 + n 4 + . . . ≈ 2 × n n + \frac{n}{2} + \frac{n}{4} + ... \approx 2 \times n n+2n+4n+...≈2×n。堆: O ( n × l o g ( k ) ) O(n \times log(k)) O(n×log(k))。 -
空间复杂度:快排: O ( l o g ( n ) ) O(log(n)) O(log(n))。堆: O ( k ) O(k) O(k)。