一列数中寻找最大K个数。
【思路】
1.常规思路,排序,数前K个数。缺点:时间O(nlogn),总个数n和k差距大的时候,比如k=1,很不实用。
如果k<=logn,用部分排序算法更快(选择排序和冒泡排序),时间O(n*k)。
2.递归,把问题规模缩小。快排的思想是用一个数把数列分成两半,一半a小于该数,一半b大于该数,
如果b的个数b.size小于k,则在a中找k-b.size个最大数,
如果b的个数b.size大于k,则在b中找k个最大数。
【code】//用伪代码翻译过来的code,不算自己写的= =
void ipartition(vector<int> src, vector<int>& s1, vector<int>& s2) { int len=src.size(); int p=src[rand()%len];//wow! for(int i=0; i<src.size(); i++){ if(src[i]<p) s1.push_back(src[i]); else s2.push_back(src[i]); } //s1.size()<s2.size()?s1.push_back(p):s2.push_back(p); } vector<int> Kbig(vector<int> s, int k) { vector<int> ret; if(k<=0) return ret; if(s.size()<=k) return s; vector<int> s1; vector<int> s2; ipartition(s, s1, s2); if(s2.size()<k){ //copy(v1.begin(),v1.end(),back_inserter(v2)) vector<int> tmp=Kbig(s1, k-s2.size()); copy(tmp.begin(),tmp.end(),back_inserter(s2)); return s2; } else if(s2.size()>k){ return Kbig(s2, k); } else return s2; }
【注意】
1.ipartition的部分,产生一个随机下标的方式:rand()%n,防止每次从s[0]开始划分会产生退化。
2.原书有注释的那一行,是将s[0]和是s[rand()%n]交换,然后从i=1开始计数,最后把划分的界放在元素书较少的那一边,
我觉得直接把用来划分的界s[rand()%n]位置不变,等于该值时随意放在哪边。
3.vector没有append方法,即将一个vector整体连在另一个vector后面,这里用copy(tmp.begin(),tmp.end(),back_inserter(s2));实现。
4.用数组和下标控制操作范围的接口似乎不太方便,故这里选用vector类型,如果遇到用数组的要求再转换。
5.时间复杂度是O(n*logk),和k的大小有关,不需要全排序。
【思路3】
用小根堆,前k个数形成一个小根堆作为k个最大数的初始化,依次用第k+i个数和堆顶的数比较,如果比堆顶大,则堆顶设为当前数,并且调整成小根堆,再与下一个数比较。
用小根堆的原因是,要把第K+i个数与最大K个数的最小数比较,每次替换最小的数,时间是O(N*K).
【code】
void min_heapify(int src[], int i, int length) { int left=i*2+1; int right=i*2+2; int smallist=i; if(left<length&&src[left]<src[smallist]) smallist=left; if(right<length&&src[right]<src[smallist]) smallist=right; if(smallist!=i){ int temp=src[i]; src[i]=src[smallist]; src[smallist]=temp; min_heapify(src, smallist, length); } } void build_min_heap(int src[], int length) { for(int i=length/2; i>=0; i--) min_heapify(src, i, length); } void findKmax(int src[], int length, int k) { if(length<k){ cout<<"error"<<endl; return; } int i; int *heap=new int[k]; for(i=0; i<k; i++) heap[i]=src[i]; build_min_heap(heap, k); while(i<length){ if(src[i]>heap[0]){ heap[0]=src[i]; min_heapify(heap, 0, k); } } for(i=0; i<k; i++) cout<<heap[i]<<' '; delete heap []; }
【总结】
1.堆代码分为两部分:调整堆、建立堆
2.堆的存储结构是数组(向量也行吧),用下标规律表示父子关系。
(此代码未经验证)