1、题目
输入n个整数,找出其中最小的k个数,例如输入4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4.
2、分析思路
思路1:最简单的莫过于先将数组排序,排序完之后最前面的k个数就是最小的k个数。时间复杂度是O(nlogn)。
思路2:时间复杂度o(n),只有当我们可以修改输入的数组时可用
我们从快速排序基于partition函数来解决这个问题。如果基于数组的第k个数字来调整,使得比第k个数字小的所有数字都位于数组的左边,比第k个数字大的所有数字都位于数组的右边。这样调整之后,位于数组中左边的k个数字就是最小的k个数字(但这k个数字不一定是排序的)。
参考代码(剑指offer)
void GetLeastNumbers(int* input, int length, int* output, int k)
{
if(input == NULL || length == 0 || output == NULL || k > length || k<=0)
return;
int start = 0;
int end = length-1;
int index=partition(input,start,end,length);
while(index != k-1)
{
if(index > k-1)
{
end = index-1;
index = partition(input,start,end,length);
}
if(index < k-1)
{
start = index+1;
index = partition(input,start,end,length);
}
}
for(int i=0;i<k; i++)
output[i]=input[i];
}思路3:O(nlogk),适合处理海量数据
先创建一个大小为k的数据容器来存储最小的k个数字,接下来每次从输入的n个整数中读入一个数。
如果容器中已有的数字少于k个,则直接把这次读入的整数放入容器之中;
如果容器之中已有k个数字了,此时我们不能再插入新的数字而只能替换已有的数字。找出这已有的k个数中的最大值,然后拿这次待插入的整数和最大值进行比较。
如果待插入值比当前已有的最大值小,则用这个数替换当前已有的最大值;
如果待插入值比当前已有的最大值还大,那么这个数不可能是最小的k个数之一,所以可以直接抛弃;
因此当容器满了之后,我们要做3件事情:一是在k个整数中找到最大数;二是有可能在这个容器中删除最大数;有可能插入一个新的数字;可以考虑使用二叉树来实现这个数据容器,那么我们能在O(logk)的时间内实现这三步操作。
最大堆可以很容易在O(1)时间内找到k个数中的最大值,删除及插入操作需要O(logk)时间。但是由于实现一个最大堆需要一定的代码,我们还可以采用红黑树来实现数据容器。在STL中set和multiset都是基于红黑树实现的。所以可以考虑用STL中的multiset来完成。
参考代码
typedef multiset<int, greater<int> > intSet; //greater<T>内置比较函数
typedef multiset<int, greater<int> >::iterator setIterator;
void GetLeastNumbers2(const vector<int>& input, intSet& output, unsigned int k)
{
output.clear();
if(k<1 || (input.size()) < k)
return;
vector<int>::const_iterator iter=input.begin();
for(;iter != input.end(); ++iter)
{
if(output.size() < k)
{
output.insert(*iter);
}
else
{
setIterator setIter=output.begin();
if(*iter < *(output.begin()) )
{
output.erase(*setIter);
output.insert(*iter);
}
}
}
}

这篇博客探讨了如何在一组整数中找出最小的k个数。提出了两种解决方案,一种是通过排序,时间复杂度为O(nlogn),另一种是利用快速排序的partition思想,使用数据容器(如最大堆或红黑树实现的multiset)在O(n)的时间复杂度内找到结果。
1448

被折叠的 条评论
为什么被折叠?



