215. 数组中的第K个最大元素and快排模版和注意点
问题
给定整数数组 nums 和整数 k,请返回数组中第 k 个最大的元素。
请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。
你必须设计并实现时间复杂度为 O(n) 的算法解决此问题。
示例 1:
输入: [3,2,1,5,6,4], k = 2
输出: 5
分析
排序再输入对应位置,或者优先队列,或者快排思想,确定第k个位置就输出。
代码
class Solution {
public:
vector<int> q;
int k;
int res = -1;
int q_n = 0;
void quick_sort(int l, int r) {
if (l >= r) {res = q[l]; return ; }
int x = q[l];
int i = l, j = r+1;
while(1) {
do {i++;} while(i < r && q[i] > x);
do {j--;} while(j > l && q[j] < x);
if (i >= j) {break ;}
int t = q[i]; q[i] = q[j]; q[j] = t;
}
int t = q[l]; q[l] = q[j]; q[j] = t;
if (k-1 == j) {res = q[j]; return ;}
else if (k-1 < j) {
quick_sort(l, j-1);
} else {
quick_sort(j+1, r);
}
}
int findKthLargest(vector<int>& nums, int k) {
this->k = k;
q_n = nums.size();
q = nums;
quick_sort(0, q_n-1);
return res;
}
};
法二、sort,复杂度O(nlogn),不符合题意,但能通过
class Solution {
public:
// 使用greater<int>()
int findKthLargest(vector<int>& nums, int k) {
int n_n = nums.size();
sort(nums.begin(), nums.end(), greater<int>());
return nums[k-1];
}
// 或者使用cmp
// static bool cmp(int e1, int e2) {
// return e1 > e2;
// }
// int findKthLargest(vector<int>& nums, int k) {
// int n_n = nums.size();
// sort(nums.begin(), nums.end(), cmp);
// return nums[k-1];
// }
};
法三、手写堆,或者优先队列
class Solution {
public:
int findKthLargest(vector<int>& nums, int k) {
priority_queue<int, vector<int>, less<int>> pq;
for (int i = 0; i < nums.size(); i++) {
pq.push(nums[i]);
}
for (int i = 0; i < k-1; i++) {
pq.pop();
}
return pq.top();
}
};
注意less对应大根堆,大的在前。
注意
if (i >= j)
do {i++;} while(i < r && q[i] > x);
do {j--;} while(j > l && q[j] < x);
if (i >= j) {break ;}
if中不能写成i>j。例如:
q = [4,6,5,5]
因为i < r的约束,所以i == 3停下,j == 3也停下,所以此时应该及时break(然后q[j]就会和q[l]换位置),否则j会继续前移,导致出现[5,6,4,5]的情况。
这里似乎也可写i > j,但是将i < r和j > l改为i <= r和j >= l ?leetcode215能过,但是我觉得还是不改了,就记 i>=j 挺好。
q[i] > x 以及 q[j] < x
这里不能写成q[i] >= x,虽然acwing测试可以过(https://www.acwing.com/problem/content/description/5337/),但是:
你会不断将 j == l,然后q[l]和自己交换,然后递归下一个区间是 [l+1, r],造成极度不平衡划分(如O(n)深度)。
对这种输入 {3,3,3,3,3},会递归:
quick_sort(1,4)
quick_sort(2,4)
quick_sort(3,4)
quick_sort(4,4)
虽然不会死循环,但效率已经从 O(n log n) 退化到了 O(n²)。
所以仍然建议
do { i++; } while (i < r && q[i] < x);
do { j--; } while (j > l && q[j] > x);
对于普通数组,表现优秀;对于等值元素,分布均衡;没有越界风险;是 Hoare 原始论文里的标准写法。
快排模版
void quick_sort(int q[], int l, int r)
{
if (l >= r) {return ;} // 这里l >= r或者l > r好像都行,但显然>=更高效
int x = q[l];
int i = l, j = r+1; // do-while,所以这里要先+1;而i=l是因为q[0]基准,所以其实是从q[1]开始
while(true)
{
do {i++;} while(i < r && q[i] < x);
do {j--;} while(j > l && q[j] > x);
if (i >= j) {break;}
int t = q[i]; q[i] = q[j]; q[j] = t;
}
int t = q[l]; q[l] = q[j]; q[j] = t;
int s=j;
quick_sort(q, l, s-1);
quick_sort(q, s+1, r);
}

浙公网安备 33010602011771号