剑指 Offer(第2版)面试题 40:最小的 k 个数

剑指Offer:最小的k个数排序、快速选择与优先队列解法
本文介绍了《剑指Offer》第2版中的面试题40——最小的k个数,提供了三种解法:排序、快速选择和优先队列,并分析了它们的时间和空间复杂度。

剑指 Offer(第2版)面试题 40:最小的 k 个数

题目来源:53. 最小的 k 个数

解法1:排序

代码:

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

复杂度分析:

时间复杂度:O(nlogn),其中 n 是数组 input 的长度。

空间复杂度:O(1)。

解法2:快速选择

运用快速排序的思想,每次快速选择会将一个数放置到正确的位置(即满足左边的数都比它小,右边的数都比它大),因此我们可以对原数组做 k 次快速选择。

但是这样做不能保证输出数组内元素按从小到大顺序排序。

代码:

class Solution
{
public:
	vector<int> getLeastNumbers_Solution(vector<int> input, int k)
	{
		vector<int> res;
		for (int i = 1; i <= k; i++)
			res.push_back(quick_select(input, 0, input.size() - 1, i));
		return res;
	}

	int quick_select(vector<int> &q, int l, int r, int k)
	{
		if (l >= r)
			return q[l];
		int i = l - 1, j = r + 1, x = q[l + 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 <= j - l + 1)
			return quick_select(q, l, j, k);
		else
			return quick_select(q, j + 1, r, k - (j - l + 1));
	}
};

复杂度分析:

时间复杂度:O(klogn),其中 n 是数组 input 的长度。一次快速选择的时间复杂度是 O(logn),进行 k 次。

空间复杂度:O(1)。

解法3:优先队列

维护一个大小为 k 的大顶堆,将数组元素都 push 进堆,当堆中的数大于 k 时弹出堆顶元素。

注意弹出堆顶的顺序是从大到小的 k 个数,要进行逆序操作。

代码:

class Solution {
public:
    vector<int> getLeastNumbers_Solution(vector<int> input, int k) {
        if (input.empty() || k == 0)
            return {};
        priority_queue<int> heap;
        for (int &x : input)
        {
            heap.push(x);
            if (heap.size() > k)
                heap.pop();
        }
        vector<int> ans;
        while (!heap.empty())
        {
            ans.push_back(heap.top());
            heap.pop();
        }
        reverse(ans.begin(), ans.end());
        return ans;
    }
};

时间复杂度:O(nlogk),其中 n 是数组 input 的长度。建堆的时间复杂度是O(logk),要进行 n 次建堆的操作。

空间复杂度:O(k)。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

UestcXiye

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值