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);
}
posted @ 2025-06-17 18:06  saulstavo  阅读(19)  评论(0)    收藏  举报