編程之美2.5——尋找最大的K個數

一列數中尋找最大K個數。算法

【思路】api

1.常規思路,排序,數前K個數。缺點:時間O(nlogn),總個數n和k差距大的時候,好比k=1,很不實用。數組

若是k<=logn,用部分排序算法更快(選擇排序和冒泡排序),時間O(n*k)。app

2.遞歸,把問題規模縮小。快排的思想是用一個數把數列分紅兩半,一半a小於該數,一半b大於該數,ui

若是b的個數b.size小於k,則在a中找k-b.size個最大數,spa

若是b的個數b.size大於k,則在b中找k個最大數。翻譯

【code】//用僞代碼翻譯過來的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;
}

【注意】blog

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.堆的存儲結構是數組(向量也行吧),用下標規律表示父子關係。

(此代碼未經驗證)

相關文章
相關標籤/搜索