一、题目
输入n个整数,找出其中最小的k个数。例如输入4、5、1、6、2、7、3、8 这8个数字,则最小的4个数字是1、2、3、4。
二、解法
分析:这道题最简单的思路莫过于把输入的n个整数排序,排序之后位于最前面的k个数就是最小的k个数,这种思路的时间按复杂度是O(nlogn).
2.1 方法一:时间复杂度为O(n)的算法,只有当我们可以修改输入的数组时可用
思路:从解决面试题39:数组中出现次数超过一半的数字得到启发,我们同样可以基于Partition函数解决这个问题,如果基于数组的第k个数字来调整,则使得比第k个数字小的所有数字都位于数组的左边,比第k个数字大的所有数字都位于数组的右边,这样调整之后,位于数组中左边的k个数字就是最小的k个数字(这k个数字不一定是排序的),下面是基于这种思路的参考代码:
class Solution {
public:
int Partition(vector<int> &input, int start, int end)
{
int x = input[end];
int i = start-1;
for(int j=start; j<=end-1; ++j)
{
if (input[j] <= x)
{
i++;
swap(input[i], input[j]);
}
}
swap(input[i+1], input[end]);
return i+1;
}
vector<int> GetLeastNumbers_Solution(vector<int> input, int k) {
int length = input.size();
vector<int> res;
if(length==0||k>length||k<=0)
return res;
int start = 0;
int end = length-1;
int index = Partition(input, start, end);
while(index!=k-1)
{
if(index>k-1)
{
end = index-1;
index = Partition(input, start, end);
}
else
{
start = index+1;
index = Partition(input, start, end);
}
}
for(int i=0;i<k;++i)
res.push_back(input[i]);
return res;
}
};
2.2 方法二:时间复杂度为O(nlogk)的算法,特别适合处理海量数据
我们可以先创建一个大小为k的数据容器来存储最小的k个数字,接下来每次从输入的n个整数中读入一个数,如果容器中已有的数字少于k个,则直接把读入的数据放入容器中,如果容器中已有了k个数字了,也就是容器已满,此时不能再插入新的数字而只能替换已有的数字,找出这已有的k个数中的最大值,然后拿这次待插入的整数和最大值进行比较,如果待插入的值比当前已有的最大值小,则用这个数替换当前已有的最大值;如果待插入的值比当前已有的最大值还大,那么这个数不可能是最小的k整数之一,于是我们可以抛弃这个整数。
因此,当容器满了以后,我们要做三件事:1)在k个整数中找到最大数;2)有可能在这个容器中删除最大数;3)有可能要插入一个新数字;如果用一个二叉树来实现这个数据容器,那么我们能在O(logk)时间内实现这三步操作。因此,对于n个输入数字而言,总的时间效率就是O(nlogk)。
class Solution {
public:
vector<int> GetLeastNumbers_Solution(vector<int> input, int k) {
int len = input.size();
if(len<=0||k>len)
return vector<int>();
multiset<int, greater<int>> leastNums;
vector<int>::iterator vec_it = input.begin();
for(;vec_it!=input.end();vec_it++)
{
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);
}
}
}
return vector<int>(leastNums.begin(), leastNums.end());
}
};
三、解法比较
基于Partition函数的解法 | 基于堆或者红黑树的解法 | |
---|---|---|
时间复杂度 | O(n) | O(nlogk) |
是否需要修改输入数组 | 是 | 否 |
是否适用于海量数据 | 否 | 是 |