最小的K个数
题目描述
给定一个数组,找出其中最小的K个数。例如数组元素是4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4。如果K>数组的长度,那么返回一个空的数组。
示例
输入
[4,5,1,6,2,7,3,8],4
输出
[1,2,3,4]
题解
方法一:直接排序
选择一种排序方法,对输的数据进行一次升序排序,通过循环取出前边最小的K个数。
时间复杂度:O( n l o g n nlog^n nlogn)
间复杂度:O(1)
class Solution {
public:
vector<int> GetLeastNumbers_Solution(vector<int> input, int k) {
vector<int> result;
if(k==0 || input.empty() || k>input.size())
return result;
sort(input.begin(), input.end());
for(int i=0; i<k; i++)
result.push_back(input[i]);
return result;
}
};
方法二:优先队列
普通的队列是先进先出的数据结构,元素在队列尾追加,从队列头删除。在优先队列中,元素被赋予优先级,最高优先级的元素最先删除。优先队列包含队列的基本操作,添加了一个内部的排序,使用堆来实现内部的排序。建立一个容量为k的大根堆的优先队列。遍历一边元素,如果队列的大小<k就直接入队,否则让当前元素与队顶元素相比,如果队顶元素大,则出队并将当前元素入队。
时间复杂度:O(
n
l
o
g
k
nlog^k
nlogk), 插入容量为k的大根堆时间复杂度为O(
l
o
g
k
log^k
logk), 一共遍历n个元素。
空间复杂度:O(k)
class Solution {
public:
vector<int> GetLeastNumbers_Solution(vector<int> input, int k) {
vector<int> result;
if (k==0 || input.empty() || k>input.size())
return result;
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()) {
result.push_back(pq.top());
pq.pop();
}
return result;
}
};
方法三:利用set数据结构
set数据结构对插入的元素可以自动排序,建立大小为k的multiset,时间复杂度为O( n l o g k nlog^k nlogk)。不能直接修改set容器内的数据,所以只能删除某个元素再插入要修改的元素。
class Solution {
public:
vector<int> GetLeastNumbers_Solution(vector<int> input, int k) {
vector<int> result;
if (k==0 || input.empty() || k>input.size())
return result;
// 仿函数greater<T>模板,从大到小排序,首元素为最大值
multiset<int, greater<int> > leastNums;
vector<int>::iterator vec_it=input.begin();
for(; vec_it!=input.end(); vec_it++) {
// 将前k个元素插入set集合
if(leastNums.size() < k)
leastNums.insert(*vec_it);
else {
// 第一个元素是最大值
multiset<int, greater<int> >::iterator greatest_it=leastNums.begin();
// 如果后续元素小于第一个元素,删除第一个,插入当前元素
if(*vec_it < *(leastNums.begin())) {
leastNums.erase(greatest_it);
leastNums.insert(*vec_it);
}
}
}
for(multiset<int>::iterator set_it=leastNums.begin(); set_it!=leastNums.end(); set_it++)
result.push_back(*set_it);
return result;
}
};
方法四:最大堆
用最大堆实现,堆的大小为k,调用algorithm里边的make_heap等现成的函数。
class Solution {
public:
vector<int> GetLeastNumbers_Solution(vector<int> input, int k) {
if (k==0 || input.empty() || k>input.size())
return vector<int>{};
vector<int> result(input.begin(), input.begin()+k);
// 建立最大堆
make_heap(result.begin(), result.end());
for(int i=k; i<input.size(); i++) {
if(result[0] > input[i]) {
// 把最大值移动到最后一个元素
pop_heap(result.begin(), result.end());
// 移除最后一个元素
result.pop_back();
result.push_back(input[i]);
// 重新选出最大值
push_heap(result.begin(),result.end());
}
}
sort_heap(result.begin(), result.end());
return result;
}
};
方法五:借鉴快排的Partition
快速排序有一个基准点,这个基准点左边都是较小的,右边都是较大值。只要这个基准点的索引值为K-1就完成了。
时间复杂度:O(n)
class Solution {
public:
int partition(vector<int> &input, int left, int right) {
int pivot = input[right-1];
int i = left;
for (int j=left; j<right-1; ++j) {
if (input[j] < pivot) {
swap(input[i++], input[j]);
}
}
swap(input[i], input[right-1]);
return i;
}
vector<int> GetLeastNumbers_Solution(vector<int> input, int k) {
vector<int> result;
if (k==0 || input.empty() || k>input.size())
return result;
int left = 0, right = input.size();
while (left < right) {
int pivot = partition(input, left, right);
if (pivot+1 == k) {
for(int i=0; i<k; i++) {
result.push_back(input[i]);
}
}
if (pivot+1 < k) {
left = pivot + 1;
}
else {
right = pivot;
}
}
return result;
}
};