设计一个算法,找出数组中最小的k个数。以任意顺序返回这k个数均可。
示例:
输入: arr = [1,3,5,7,2,4,6,8], k = 4
输出: [1,2,3,4]
提示:
0 <= len(arr) <= 100000
0 <= k <= min(100000, len(arr))
解题思路: 这是经典的TOP-K问题,大致的解法主要有堆排序,快速排序。堆排序的时间复杂度是 O ( n l o g k ) O(nlogk) O(nlogk).下面先用堆排序解再用快速排序解题。TOP-K问题这篇文章讲的非常清楚,可以参考。4种解法秒杀TopK(快排变形/堆/二叉搜索树/计数排序)
堆排的时间复杂度: O ( n l o g k ) O(nlogk) O(nlogk),快排时间复杂度: O ( n ) O(n) O(n).
class Solution {
public:
vector<int> smallestK(vector<int>& arr, int k) {
priority_queue<int> q;
for (int &a : arr) {
q.push(a);
if (q.size() > k) q.pop();
}
vector<int> res;
while (!q.empty()) {
res.push_back(q.top()); q.pop();
}
return res;
}
};
用快排解此题时,需要对快排模板非常熟,快排的基本思路是在一次排序之后,大于某个值的序列全在它的右边,而小于这个值的序列全部位于他的左边。具体的步骤是,每次选序列的第一个元素(val)作为参考值,然后两个指针序列的两端(left,right),然后left向后移动直至找到比val大的数,right向前移动直至找到比val小的数,然后将这两个数交换,最后right停留的位置的数小于等于val,将它与参考值交换,参考值交换到right处即可达到此次排序后小于val的值在他左边,而大于val的值在它右边。那么用快排解决TOP-K的问题是这样的,因为快排的核心是每次能够确定第j大的数,那么如果我们某次迭代时找到的j
注:快排模板写在下面附录中。
class Solution {
public:
int partition(vector<int>&arr, int lo, int hi) {
int v = arr[lo];
int left = lo, right = hi + 1;
while (true) {
while (++left <= hi && arr[left] < v);
while (--right >= lo && arr[right] > v);
if (left >= right) break;
swap(arr[left], arr[right]);
}
arr[lo] = arr[right];
arr[right] = v;
return right;
}
vector<int> quickSearch(vector<int> &arr, int lo, int hi, int k) {
int j = partition(arr, lo, hi);
if (j == k) return vector<int>(arr.begin(), arr.begin() + j + 1);
return j > k ? quickSearch(arr, lo, j - 1, k) : quickSearch(arr, j + 1, hi, k);
}
vector<int> smallestK(vector<int>& arr, int k) {
if (k == 0 || arr.empty()) return {};
return quickSearch(arr, 0, arr.size() - 1, k - 1);
}
};
快排模板:
#include
#include
using namespace std;
int partition(vector<int> &nums, int lo, int hi) {
int v = nums[lo];
int i = lo, j = hi + 1;
while (true) {
while (++i <= hi && nums[i] < v) ;
while (--j >= lo && nums[j] > v) ;
if (i >= j) break;
swap(nums[i], nums[j]);
}
nums[lo] = nums[j];
nums[j] = v;
return j;
}
void quickSearch(vector<int> &nums, int lo, int hi) {
if (lo > hi) return;
int j = partition(nums, lo, hi);
quickSearch(nums, lo, j - 1);
quickSearch(nums, j + 1, hi);
}
int main() {
vector<int> v = {1,5,2,7,23};
quickSearch(v, 0, v.size() - 1);
for (auto t : v) {
cout << t << ",";
}
return 0;
}