编程之美2.5——寻找最大的K个数

一列数中寻找最大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.堆的存储结构是数组(向量也行吧),用下标规律表示父子关系。

(此代码未经验证)

原文地址:https://www.cnblogs.com/ketchups-notes/p/4505946.html