剑指 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)。