题目:
输入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个数,所以他是适用的。