O(N)的時間尋找最大的K個數

尋找N個數中最大的K個數,本質上就是尋找最大的K個數中最小的那個,也就是第K大的數。可以使用二分搜索的策略來尋找N個數中的第K大的數。對於一個給定的數p,可以在O(N)的時間複雜度內找出所有不小於p的數。尋找第k大的元素:

#include <iostream>
using namespace std;

//快速排序的劃分函數
int partition(int a[],int l,int r)
{
    int i,j,x,temp;
    i = l;
    j = r+1;
    x = a[l];
    //將>=x的元素換到左邊區域
    //將<=x的元素換到右邊區域
    while (1)
    {
        while(a[++i] > x);
        while(a[--j] < x);
        if(i >= j) break;
        temp = a[i];
        a[i] = a[j];
        a[j] = temp;
    }
    a[l] = a[j];
    a[j] = x;
    return j;
}

//隨機劃分函數
int random_partition(int a[],int l,int r)
{
    int i = l+rand()%(r-l+1);//生產隨機數
    int temp = a[i];
    a[i] = a[l];
    a[l] = temp;
    return partition(a,l,r);//調用劃分函數
}

//線性尋找第k大的數
int random_select(int a[],int l,int r,int k)
{
    int i,j;
    if (l == r) //遞歸結束
    {
        return a[l];
    }
    i = random_partition(a,l,r);//劃分
    j = i-l+1;
    if(k == j) //遞歸結束,找到第K大的數
        return a[i];
    if(k < j)
    {
        return random_select(a,l,i-1,k);//遞歸調用,在前面部分查找第K大的數
    }
    else
        return random_select(a,i+1,r,k-j);//遞歸調用,在後面部分查找第K大的數}

int main()
{
    int a[]={1,2,3,4,6,6,7,8,10,10};

    cout<<random_select(a,0,9,1)<<endl;
    cout<<random_select(a,0,9,5)<<endl;
    return 0;
}

如果所有N個數都是正整數,且它們的取值範圍不太大,可以考慮申請空間,記錄每個整數出現的次數,然後再從大到小取最大的K個。比如,所有整數都在(0, MAXN)區間中的話,利用一個數組count[MAXN]來記錄每個整數出現的個數(count[i]表示整數i在所有整數中出現的個數)。只需要掃描一遍就可以得到count數組。然後,尋找第K大的元素:

for(sumCount = 0, v = MAXN-1; v >= 0; v--)
{
    sumCount += count[v];
    if(sumCount >= K)
        break;
}
return v;

極端情況下,如果N個整數各不相同,我們甚至只需要一個bit來存儲這個整數是否存在(bit位爲1或爲0),這樣使用的空間可以大大壓縮。

當然也可以使用像計數排序、桶排序等這些以O(N)的時間排序算法也可以尋找第K大的數,但這也是以空間換時間爲代價的。

實際情況下,並不一定保證所有元素都是正整數,且取值範圍不太大。上面的方法仍然可以推廣使用。如果N個數中最大的數Vmax,最小的Vmin,我們可以把這個區間[Vmax,Vmin]分成M塊,每個小區間的跨度爲d=(Vmax-Vmin)/M,即[Vmin,Vmin+d],[Vmin+d,Vmin+2d]......然後,掃描一遍所有元素,統計各個小區間中的元素個數,就可以知道第K大的元素在哪一個小區間。然後,再在那個小區間中找第K大的數(此時這個小區間中,第K大的數可能就是第T大的數了,這個T和每個小區間的個數有關)。我們需要找一個儘量大的M,但M的取值受到內存的限制。

尋找最大的K個數,使用堆實現,可以看這篇文章:http://blog.csdn.net/luxiaoxun/article/details/7796368

 



 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章