最小的k个数

最小的K个数

http://www.nowcoder.com/questionTerminal/6a296eb82cf844ca8539b57c23e6e9bf

描述

这是一篇针对初学者的题解。共用三种方法解决。
知识点:数组,堆,快排
难度:二星


题解

题目抽象:求给定数组的topK小问题。

方法一:排序

直接排序,然后去前k小数据。

代码

class Solution {
public:
    vector<int> GetLeastNumbers_Solution(vector<int> input, int k) {
        vector<int> ret;
        if (k==0 || k>input.size()) return ret;
        sort(input.begin(), input.end());
        return vector<int>({input.begin(), input.begin()+k});   
    }
};

时间复杂度:O(nlongn)
空间复杂度:O(1)

方法二:堆排序

建立一个容量为k的大根堆的优先队列。遍历一遍元素,如果队列大小<k,就直接入队,否则,让当前元素与队顶元素相比,如果队顶元素大,则出队,将当前元素入队

代码

class Solution {
public:
    vector<int> GetLeastNumbers_Solution(vector<int> input, int k) {
        vector<int> ret;
        if (k==0 || k > input.size()) return ret;
        priority_queue<int, vector<int>> pq;
        for (const int val : input) {
            if (pq.size() < k) {
                pq.push(val);
            }
            else {
                if (val < pq.top()) {
                    pq.pop();
                    pq.push(val);
                }

            }
        }

        while (!pq.empty()) {
            ret.push_back(pq.top());
            pq.pop();
        }
        return ret;
    }
};

时间复杂度:O(nlongk), 插入容量为k的大根堆时间复杂度为O(longk), 一共遍历n个元素
空间复杂度:O(k)

方法三:快排思想

对数组[l, r]一次快排partition过程可得到,[l, p), p, [p+1, r)三个区间,[l,p)为小于等于p的值
[p+1,r)为大于等于p的值。
然后再判断p,利用二分法

  1. 如果[l,p), p,也就是p+1个元素(因为下标从0开始),如果p+1 == k, 找到答案
    2。 如果p+1 < k, 说明答案在[p+1, r)区间内,
    3, 如果p+1 > k , 说明答案在[l, p)内

代码

class Solution {
public:
    int partition(vector<int> &input, int l, int r) {
        int pivot = input[r-1];
        int i = l;
        for (int j=l; j<r-1; ++j) {
            if (input[j] < pivot) {
                swap(input[i++], input[j]);
            }
        }
        swap(input[i], input[r-1]);
        return i;

    }
    vector<int> GetLeastNumbers_Solution(vector<int> input, int k) {
        vector<int> ret;
        if (k==0 || k > input.size()) return ret;
         int l = 0, r = input.size();
        while (l < r) {
            int p = partition(input, l, r);
            if (p+1 == k) {
                return vector<int>({input.begin(), input.begin()+k});
            }
            if (p+1 < k) {
                l = p + 1;
            }   
            else {
                r = p;
            }

        }
        return ret;
    }
};

时间复杂度:平均时间复杂度为O(n),每次partition的大小为n+n/2+n/4+... = 2n,最坏时间复杂度为O(n^2), 因为每次partition都只减少一个元素
空间复杂度:O(1)

全部评论
l到p怎么就p+1个元素呢 这写的都是啥
1 回复 分享
发布于 2021-04-21 10:48
第三种方法返回的ret是无序的吧?求解答
5 回复 分享
发布于 2020-06-12 20:16
有点疑问第二种方法,运用优先队列,默认是按照大顶堆top元素堆顶最大,那么后面在往ret中push_back元素时应该是倒叙的啊(从大到小),单步调试ret也是倒叙,为啥提交上去能通过。
4 回复 分享
发布于 2021-09-04 16:30
第一次见到这样写快排的,只需要一次遍历,牛逼
3 回复 分享
发布于 2021-04-02 15:00
第三种方法的快排是错的,因为在i节点上的数一直没有跟pivot作比较,在for loop结束后的swap如果i节点上的数小于pivot点,那最终也会被放在pivot右边(正确的是小于pivot的在pivot左边,大于的在右边)。
2 回复 分享
发布于 2021-07-19 03:41
请问 这段代码for (const int val : input) {}可以替换成迭代器吗
点赞 回复 分享
发布于 2021-01-04 20:35
第一个方法简直亮瞎我的双眼。
点赞 回复 分享
发布于 2022-03-07 21:47
第一个空间复杂度为o(1)???????????????
点赞 回复 分享
发布于 2022-05-04 15:53
这是怎么去重的呢
点赞 回复 分享
发布于 2023-01-10 11:56 美国
第三种 : class Solution { public: int partition(vector<int> &input, int l, int r) { int target = input[r - 1], i = l, j = l; for (; j < r - 1 ; j++) { if (target > input[j]) { if (i != j) swap(input[i++], input[j]); else i++; } } swap(input[i], input[r - 1]); return i; } vector<int> GetLeastNumbers_Solution(vector<int> input, int k) { int size = input.size(), l = 0, r = size; if (k == 0 || k > size) return {}; while (l < r) { int index = partition(input, l, r); if (index + 1 == k || index == k) return vector<int>(input.begin(), input.begin() + k); if (index + 1 < k) l = index + 1; else r = index; } return {}; } };</int></int></int></int>
点赞 回复 分享
发布于 2023-02-28 17:13 广东
对排列也是错的呀
点赞 回复 分享
发布于 01-16 09:50 广东

相关推荐

10-25 00:32
香梨想要offer:感觉考研以后好好学 后面能乱杀,目前这简历有点难
点赞 评论 收藏
分享
废铁汽车人:秋招真是牛鬼蛇神齐聚一堂
点赞 评论 收藏
分享
评论
120
33
分享
牛客网
牛客企业服务