【轉(zhuǎn)載】N個(gè)數(shù)里面找出最大的k個(gè)數(shù)

https://www.douban.com/note/275544555/


題目:給出N個(gè)無序的數(shù)玄呛,然后找出其中最大的k個(gè)數(shù)

解題思路:

?????????首先測(cè)試數(shù)據(jù)有可能會(huì)有一億個(gè)數(shù)件缸,數(shù)據(jù)量特別的大基括,數(shù)據(jù)庫(kù)不可能存儲(chǔ)這么多的數(shù)據(jù)杨赤。如果直接sort排序,NlogN時(shí)間復(fù)雜度實(shí)在是太高句葵,大于10^9。我們可以考慮對(duì)數(shù)據(jù)進(jìn)行分塊讀取兢仰,每次讀取的數(shù)據(jù)塊大小應(yīng)大于k乍丈。

?????????不如先假設(shè)第一次讀取的數(shù)據(jù)塊前k個(gè)數(shù)最大,然后把k個(gè)數(shù)建成最小二叉堆把将。然后從第k+1個(gè)數(shù)開始轻专,每個(gè)數(shù)都與堆頂?shù)臄?shù)值進(jìn)行比較,如果數(shù)字i大于堆頂則把堆頂?shù)脑氐脑靥鎿Q成i察蹲,再調(diào)整一次堆请垛。最后讀取完數(shù)據(jù)之后,這個(gè)二叉堆里面的元素就是從小到大排序好的最大k個(gè)數(shù)洽议。

時(shí)間復(fù)雜度:O(NlogK)

空間復(fù)雜度:O(K)

證明過程:

?????????為什么求最大的k個(gè)用的不是最大堆宗收,而是最小堆?最大堆堆頂?shù)脑厥亲畲蟮难切郑碌淖訕湓絹碓叫』旎袾個(gè)數(shù)建成最大堆,那么堆頂往下的k個(gè)數(shù)就是最大的k個(gè)數(shù)审胚。但是時(shí)間復(fù)雜度O(NlogN)和空間復(fù)雜度O(N)太高匈勋!

?????????排序時(shí)間復(fù)雜度很高,是因?yàn)檫M(jìn)行了很多沒有用的判斷膳叨,我們只需要取最大的k個(gè)數(shù)洽洁,而排序則把N個(gè)數(shù)都從小到大排序好了。建立一個(gè)k個(gè)數(shù)的最小堆菲嘴,假設(shè)堆里面的元素是最大的饿自,當(dāng)然只是假設(shè)碎浇。如果從M+1到N這些數(shù)只要有數(shù)大于最小堆堆頂?shù)臄?shù),那么假設(shè)就不成立璃俗,堆頂那個(gè)數(shù)就不符合奴璃,自然把它去掉,把新的數(shù)加進(jìn)來城豁,再重新調(diào)整堆苟穆,使得堆頂?shù)脑刈钚 ?/p>

?????????為什么要用最小堆呢?因?yàn)槊看尾檎疫@k個(gè)數(shù)里面的最小的那個(gè)數(shù)就是堆頂唱星,時(shí)間復(fù)雜度是O(1)雳旅。如果直接用數(shù)組來存儲(chǔ)這k個(gè)數(shù),雖然查找的時(shí)間復(fù)雜度是logN间聊,但是當(dāng)把這個(gè)數(shù)插入數(shù)組的時(shí)候攒盈,數(shù)組比它小其他元素還需要往前平移,所以時(shí)間復(fù)雜度遠(yuǎn)遠(yuǎn)大于logN哎榴。由于每次調(diào)整堆的時(shí)間復(fù)雜度是logN型豁。所以最小堆的做法的時(shí)間復(fù)雜度是

O(NlogK),而空間復(fù)雜度只有O(K)尚蝌。


代碼1? C++的STL庫(kù)優(yōu)先隊(duì)列實(shí)現(xiàn)二叉堆:

#include

#include

#include

#include

#include

using namespace std;

struct cmp{

???bool operator ()(int a,int b)

???{

???????return a>b;

???}

};

#define MAX 11000

int a[MAX];

using namespace std;

priority_queue,cmp>q;

int main()

{

???int n,i,k,m,top;

???scanf("%d%d",&n,&m);

?????for(i=1;i<=n;i++)

?????{

??????????scanf("%d",&k);

??????????if(i<=m)? //前m個(gè)數(shù)入 隊(duì)列

??????????{

???????????????q.push(k);

???????????????if(i==m)? //紀(jì)錄前m個(gè)數(shù)中最小的數(shù)

????????????????????top=q.top();

??????????}

??????????else

??????????{

???????????????if(k>top)? //如果新加入的數(shù)大于隊(duì)列中最小的數(shù)則出隊(duì)

???????????????{

????????????????????q.pop();

????????????????????q.push(k);

????????????????????top=q.top();

???????????????}

??????????}

?????}

?????k=0;

?????while(!q.empty())? //這樣處理是為了最后一個(gè)數(shù)打印時(shí)沒有空格

?????{

??????????a[k++]=q.top();

??????????q.pop();

?????}

?????for(i=0;i

?????{

??????????printf("%d",a[i]);

??????????if(i==k-1)

???????????????printf("\n");

??????????else

???????????????printf(" ");

?????}

???return 0;

}

代碼2 (數(shù)組實(shí)現(xiàn)堆):

#include

#define MAX 10001

int a[MAX];

void HeapAdjust(int R[],int s,int t)? //篩選函數(shù)1

{

???int i,j,temp;

???temp=R[s];

???i=s;

???for(j=2*i;j<=t;j=2*j)

???{

???????if(j

???????????j++;

???????if(temp>R[j]) break;

???????R[i]=R[j];

???????i=j;

???}

???R[i]=temp;

}

void HeapSort(int R[],int n)? //堆排

{

???int i;

???for(i=n/2;i>0;i--)

???{

???????HeapAdjust(R,i,n);

???}

???for(i=n;i>1;i--)

???{

???????R[1]^=R[i];

???????R[i]^=R[1];

???????R[1]^=R[i];

???????HeapAdjust(R,1,i-1);

???}

}

void HeapAdjust2(int R[],int s,int t)? //篩選函數(shù)2

{

???int i,j,temp;

???temp=R[s];

???i=s;

???for(j=2*i;j<=t;j=2*j)

???{

???????if(jR[j+1])

???????????j++;

???????if(temp

???????R[i]=R[j];

???????i=j;

???}

???R[i]=temp;

}

int main()

{

???int i,k,n,m;

???scanf("%d%d",&n,&m);

???for(i=1;i<=m;i++)

???{

???????scanf("%d",&a[i]);

???}

???HeapSort(a,m);

???for(i=m+1;i<=n;i++)

???{

???????scanf("%d",&k);

???????if(k>a[1])? ? ? ? //新元素大于堆中最小元素則加入堆

???????{

???????????a[1]=k;

???????????HeapAdjust2(a,1,m);? //從根節(jié)點(diǎn)開始重新篩選一次

???????}

???}

???HeapSort(a,m);

???for(i=1;i<=m;i++)

???{

???????printf("%d",a[i]);

???????if(i==m)

???????????printf("\n");

???????else

???????????printf(" ");

???}

???return 0;

}

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末迎变,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子飘言,更是在濱河造成了極大的恐慌衣形,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,525評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件姿鸿,死亡現(xiàn)場(chǎng)離奇詭異百新,居然都是意外死亡填帽,警方通過查閱死者的電腦和手機(jī)隶症,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,203評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門许起,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人碟渺,你說我怎么就攤上這事鲜锚。” “怎么了苫拍?”我有些...
    開封第一講書人閱讀 164,862評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵芜繁,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我绒极,道長(zhǎng)骏令,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,728評(píng)論 1 294
  • 正文 為了忘掉前任垄提,我火速辦了婚禮榔袋,結(jié)果婚禮上周拐,老公的妹妹穿的比我還像新娘。我一直安慰自己凰兑,他們只是感情好妥粟,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,743評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著吏够,像睡著了一般勾给。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上锅知,一...
    開封第一講書人閱讀 51,590評(píng)論 1 305
  • 那天播急,我揣著相機(jī)與錄音,去河邊找鬼售睹。 笑死桩警,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的昌妹。 我是一名探鬼主播捶枢,決...
    沈念sama閱讀 40,330評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼捺宗!你這毒婦竟也來了柱蟀?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,244評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤蚜厉,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后畜眨,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體昼牛,經(jīng)...
    沈念sama閱讀 45,693評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,885評(píng)論 3 336
  • 正文 我和宋清朗相戀三年康聂,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了贰健。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,001評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡恬汁,死狀恐怖伶椿,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情氓侧,我是刑警寧澤脊另,帶...
    沈念sama閱讀 35,723評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站约巷,受9級(jí)特大地震影響偎痛,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜独郎,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,343評(píng)論 3 330
  • 文/蒙蒙 一踩麦、第九天 我趴在偏房一處隱蔽的房頂上張望枚赡。 院中可真熱鬧,春花似錦谓谦、人聲如沸贫橙。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,919評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽卢肃。三九已至,卻和暖如春星压,著一層夾襖步出監(jiān)牢的瞬間践剂,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,042評(píng)論 1 270
  • 我被黑心中介騙來泰國(guó)打工娜膘, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留逊脯,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,191評(píng)論 3 370
  • 正文 我出身青樓竣贪,卻偏偏與公主長(zhǎng)得像军洼,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子演怎,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,955評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容