题目:
输入n个整数,找出其中最小的k个数,例如输入4,5,1,6,2,7,3,8这个8个数字,则最小的4个数字是1,2,3,4.
基本思想:
解法一:先排序再找K:O(nlgn)
最简单的方法就是把它排序,然后找出前面的k个数字就是最小的k个数字,这种思路的时间复杂度是O(nlgn)
解法二:partition函数:O(n)
利用快速排序的partition函数来将数组分成两部分,partition函数的返回值就是排序好的数组的最终下标,其左边是比它小的数,右边是比它大的数,这样我们只需要将partition的返回值与k做比较,如果与k相等,则返回(返回的k个数字不一定是排序的);如果比k大,则最小的k个数在左边,则继续返回递归;如果比k小,则最小的k个数中有一部分在右边,则返回这部分到右边寻找。
#include <iostream>
using namespace std;
int par(int a[],int len,int low,int high)
{
int t=a[low];
int i=low,j=high;
while(i!=j)
{
while(i<j&&a[j]>=t) j--;
while(i<j&&a[i]<=t) i++;
if(i<j)
{
int temp=a[i];
a[i]=a[j];
a[j]=temp;
}
}
a[low]=a[i];
a[i]=t;
return i;
}
void foo(int a[],int len,int k)
{
if(len<=0)
return ;
int start=0;
int end=len-1;
int index=par(a,len,start,end);
while(index!=k-1)
{
if(index>k-1)
{
end=index-1;
index=par(a,len,start,end);
}
else
{
start=index+1;
index=par(a,len,start,end);
}
}
for(int i=0;i<k;i++)
cout<<a[i]<<" ";
cout<<endl;
}
int main()
{
int a[]={4,5,1,6,2,7,3,8};
int k=4;
int len = sizeof(a)/sizeof(a[0]);
foo(a,len,k);
return 0;
}
解法三:海量数据:O(nlgk)
我们可以先创建一个大小为K的数据容器来存储最小的K个数,接下来我们每次从输入的n个整数中读入一个数,如果容器中已有的数字少于K,则直接把这次读入的整数放入容器中;如果容器中已有K个数,就是容器满了,此时我们不能插入新的数字而只能替换已有的数字了。找出这K个数的最大值,然后拿这次待插入的整数和最大值相比较。如果待插入的值比当前已有的最大值小,则用这个数替换当前已有的最大值;如果待插入的值比当前已有的最大值还大,那么这个数不可能是最小的K个整数之一,于是我们可以抛弃这个数。
我们可以用二叉树(红黑树或最大堆)来实现这个数据容器。堆查找最大值O(1),删除插入为O(logk);红黑树查找、删除和插入都是O(logk)。
第2种情况非常适合海量数据的处理,当n>>k的时候非常适合。
时间复杂度为:O(n*lgk)
typedef multiset<int, greater<int> > intSet;
typedef multiset<int, greater<int> >::iterator setIterator;
void GetLeastNumbers_Solution2(const vector<int>& data, intSet& leastNumbers, int k)
{
leastNumbers.clear();
if(k < 1 || data.size() < k)
return;
vector<int>::const_iterator iter = data.begin();
for(; iter != data.end(); ++ iter)
{
if((leastNumbers.size()) < k)
leastNumbers.insert(*iter);
else
{
setIterator iterGreatest = leastNumbers.begin();
if(*iter < *(leastNumbers.begin()))
{
leastNumbers.erase(iterGreatest);
leastNumbers.insert(*iter);
}
}
}
}
解法二与解法三比较:
解法二是基于Partition其时间复杂度为O(n),比解法三要快,但是解法二是有限制的,它改变了原始数组,如果要求不能改变数组的话,就不适用解法二了。
解法三虽然慢了点,但他是有优点的,1.没有改变数组,2.使用于海量数据,如果数据量特别大,那么一次可能不能把所有数据都读入内存,那么这样的话解法二是明显不行的,因它要把所有数据都存如内存才能再做转化和比较,然而解法三就会特别适合,因为他是一个个读数据,并且容器中只存k个数,所以他是适用的。