剑指Offer:面试题30 最小的k个数

来源:互联网 发布:js给textbox赋值 编辑:程序博客网 时间:2024/05/29 03:24
/*最小的k个数:输入n个整数,找出其中最小的k个数。例如输入4、5、1、6、2、7、3、8这8个数字,则最小的4个数字时1、2、3、4。分析:解法1:可以修改输入的。只需利用划分函数,对下标为k的数字进行划分,那么最左边的k各数字一定是最小的k个数字,但是不一定有序。因为数组的下标是从0开始的,则当iIndex = k -1时,即可解法2:O(nlogK)的算法,适合处理海量数据(适合n很大,k很小)先创建一个大小为k的数据容器来存储最小的k各数字,每次从输入的n个整数中读入一个数。如果容器中已有的数字少于k个,直接把这次读入的整数放入容器中。如果容器中已经有k个数,此时只能替换原有数字。找出k个数字中的最大值,然后依次拿待插入的整数和最大值进行比较,若待插入的小,就替换。容器满了做3件事:1在k个整数中找最大数2在容器中删除最大数3可能要插入一个新数字必须用二叉树实现这个容器,则可以在O(logK)实现三步,对于n个数字,总效率就是O(nlogK)而红黑树可以确保:查找、删除、插入都只需要O(logK)时间,可以用multiset来实现对于海量数据中寻找最小的k个数字,由于不能将海量数据一次性载入,因此可用辅存来每次读入一个数字,来判定是否需要放入容器中输入:每个测试案例包括2行:第一行为2个整数n,k(1<=n,k<=200000),表示数组的长度。第二行包含n个整数,表示这n个数,数组中的数的范围是[0,1000 000 000]。输出:对应每个测试案例,输出最小的k个数,并按从小到大顺序打印。样例输入:8 44 5 1 6 2 7 3 810 51 9 8 6 4 1 0 4 3 5样例输出:1 2 3 40 1 1 3 4 *//*关键:1 利用划分函数,对下标为k的数字进行划分,那么最左边的k各数字一定是最小的k个数字,但是不一定有序。2 while(iIndex != k-1 )//注意,这里不是iIndex = k,因为数组的第k-1下标,表示数组中第k个数3 O(nlogK)的算法,适合处理海量数据(适合n很大,k很小)先创建一个大小为k的数据容器来存储最小的k各数字,每次从输入的n个整数中读入一个数。如果容器中已有的数字少于k个,直接把这次读入的整数放入容器中。如果容器中已经有k个数,此时只能替换原有数字。找出k个数字中的最大值,然后依次拿待插入的整数和最大值进行比较,若待插入的小,就替换。4 vector<int>::const_iterator itV;//对于常量容器,必须要用常量迭代器5 if(setLeastK.size() < k)//如果存储最小k个元素的容器大小<K,则直接加入{setLeastK.insert(*itV);}else//如果已经达到k个元素,则找出其中的最大元素,删除该最大元素后,再加入新的小元素6int low = 0,high = iLen -1;//注意,这里做划分的函数的下标必须能取到*/#include <stdio.h>#include <string.h>#include <stdlib.h>#include <set>#include <vector>#include <stack>using namespace std;const int MAXSIZE = 200001;int iArr[MAXSIZE];int randomInRange(int min,int max){return (rand() % (max - min + 1) + min);}void swap(int* pNum1,int* pNum2){int iTemp = *pNum1;*pNum1 = *pNum2;*pNum2 = iTemp;}int partition(int low,int high){int iIndex = randomInRange(low,high);swap(&iArr[low],&iArr[iIndex]);int iAxis = iArr[low];while(low < high){while(low < high && iArr[high] >= iAxis){high--;}iArr[low] = iArr[high];while(low < high && iArr[low] <= iAxis){low++;}iArr[high] = iArr[low];}iArr[low] = iAxis;return low;}void minKNum(int iLen,int k){int low = 0,high = iLen -1;//注意,这里做划分的函数的下标必须能取到int iIndex = partition(low,high);while(iIndex != k-1 )//注意,这里不是iIndex = k,因为数组的第k-1下标,表示数组中第k个数{if(iIndex > k-1 ){high = iIndex - 1;}else{low = iIndex + 1;}iIndex = partition(low,high);}for(int i = 0 ; i < k ; i++){if(i){printf(" %d",iArr[i]);}else{printf("%d",iArr[i]);}}printf("\n");}void minKNum_BigData(const vector<int>& vecData,multiset<int,greater<int> >& setLeastK,int k)//用vector来存储数据,用multiset来存储最小的k个数,注意这里用greater<int>来确保容器中的第一个数字是最大的数字{vector<int>::const_iterator itV;//对于常量容器,必须要用常量迭代器for(itV = vecData.begin() ; itV != vecData.end() ; itV++){if(setLeastK.size() < k)//如果存储最小k个元素的容器大小<K,则直接加入{setLeastK.insert(*itV);}else//如果已经达到k个元素,则找出其中的最大元素,删除该最大元素后,再加入新的小元素{multiset<int,greater<int> >::iterator itMax = setLeastK.begin();if(*itMax > *itV){setLeastK.erase(itMax);//注意这里void erase(iterator it),删除的是迭代器不是真实的数字setLeastK.insert(*itV);}}}multiset<int,greater<int> >::iterator itSet;int iCnt = 0;stack<int> stackLeastK;for(itSet = setLeastK.begin() ; itSet !=  setLeastK.end(); itSet++)//因为打印的时候需从小到大打印,这里费劲了,用栈{if(iCnt == k){break;}stackLeastK.push(*itSet);}bool isFirst = true;while(!stackLeastK.empty()){if(!isFirst){printf(" %d",stackLeastK.top());}else{isFirst = false;printf("%d",stackLeastK.top());}stackLeastK.pop();}printf("\n");}void process(){int n,k;while(EOF != scanf("%d %d",&n,&k)){if(n < 1 || n >= MAXSIZE || k < 1 || k >= MAXSIZE){continue;}memset(iArr,0,sizeof(iArr));for(int i = 0 ; i < n ; i++){scanf("%d",&iArr[i]);}vector<int> vecData(iArr,iArr+n);multiset<int,greater<int> > setLeastK;minKNum(n,k);minKNum_BigData(vecData,setLeastK,k);}}int main(int argc,char* argv[]){process();getchar();return 0;}


 

0 0