题目
题目:https://leetcode.cn/problems/kth-largest-element-in-an-array/description/
解法
这道题目目前快排可以直接过,但是时间复杂度是 O ( n l o g n ) O(nlogn) O(nlogn)。
想要 O ( n ) O(n) O(n),这就涉及到408考研知识点了😂(bushi)。快排每次 partition 过程一定会确定 pivot 的位置,我们根据 k 的值对其中一侧继续 partition 直到 pivot = k。
理想情况,假设每次 pivot 都在中间位置,那么时间复杂度为 n + n / 2 + n / 4 + ⋯ = O ( n ) n+n/2+n/4+\cdots=O(n) n+n/2+n/4+⋯=O(n)。
最差情况是数组已有序,每次 pivot 都在数组末尾,而 k 在另一侧,时间复杂度为 n + ( n − 1 ) + ( n − 2 ) + ⋯ = O ( n 2 ) n+(n-1)+(n-2)+\cdots=O(n^2) n+(n−1)+(n−2)+⋯=O(n2)。为了避免这种情况,引入随机化。
因为找最大值,所以降序排列。
class Solution {
public:
int partition(vector<int>& nums, int start, int end) {
// 随机化确定 num[end]
int ran = rand() % (end-start+1) + start;
swap(nums[ran], nums[end]);
// p 指针右边的数都小于(大于)pivot
int p = start-1, x = nums[end];
for (int i = start; i < end; i++) {
if (nums[i] >= x) {
swap(nums[++p], nums[i]);
}
}
swap(nums[++p], nums[end]);
// 返回 pivot
return p;
}
int qsort(vector<int>& nums, int start, int end, int k) {
// 无需最顶层栈的 return,partition 函数已经涵盖
int pivot = partition(nums, start, end);
if (pivot < k) {
return qsort(nums, pivot+1, end, k);
} else if (pivot > k) {
return qsort(nums, start, pivot-1, k);
} else {
return nums[k];
}
}
int findKthLargest(vector<int>& nums, int k) {
int len = nums.size();
return qsort(nums, 0, len-1, k-1);
}
};
进阶
假设数组很大很大,内存无法全部存放,如何找到第 k 个最大值(不要求时间复杂度 O ( n ) O(n) O(n),但要保证耗时尽可能少)?
内存无法存放,那就不能用排序(涉及到内存和外存的交换,速度会很慢)。可以维护一个大小为 k 的堆,遍历数组更新堆即可。时间复杂度为 O ( n l o g ( n ) ) O(nlog(n)) O(nlog(n))
这个堆具体是优先队列,其内部用堆实现。优先队列内的元素是有序的,push 新元素后,可以在 O ( l o g n ) O(logn) O(logn) 时间内维护好,通过 pop 可以删除最大值或者最小值,通过 top 可以查看其最值。
priority_queue 默认是降序排列(和 sort 相反),而且 greater<int> 不加括号(sort 是要加的)
class Solution {
public:
int findKthLargest(vector<int>& nums, int k) {
priority_queue<int, vector<int>, greater<int>> que;
int len = nums.size();
for (int i = 0; i < len; i++) {
que.push(nums[i]);
if (i >= k) {
que.pop();
}
}
return que.top();
}
};