[資料結構].堆(優先佇列)
阿新 • • 發佈:2019-01-31
堆(優先佇列)
堆的定義
一種樹形資料結構,分大根堆,小根堆。
大根堆(max heap)滿足所有父節點不小於其任意子節點。
小根堆(min heap)滿足所有父節點不大於其任意子節點。
在這裡,我們只考慮二叉堆。二叉堆是一棵完全二叉樹。
堆的操作
- 插入新元素(Ο(logN))
- 查詢最值(Ο(1))
- 刪除最大值(Ο(logN))
- 修改元素(Ο(logN))
C++實現
STL實現
例題
1.合併果子
- 有N堆果子,每次可以選擇任意兩堆合併,需要花費的代價是兩堆果子的數量之和。
- 可見,經過N-1次合併後,果子只剩下一堆。
- 求將果子合併成一堆需要花費的最少代價。
- 初始果子堆數不超過100000.
Solution
可見,每堆果子分別被合併了N-1次,N-2次,N-3次……1次
則數量越小的堆合併次數應越多。
建立一個小根堆,每次合併堆頂與其小兒子計入總數,相當於修改小兒子(Ο(logN)),堆頂刪除(Ο(logN))。
複雜度(Ο(NlogN))
2.黑匣子
起先黑匣子為空。現在執行一系列命令,命令有兩類:
- ADD(x):把元素x放入黑匣子;
- GET:輸出黑匣子內第K大的數,其中K為當前執行GET操作的總次數。
命令條數不超過500000。
Solution
先考慮尋找第K大數,即尋找數列中第K大的數。
因為要輸出第K大的數,故維護一個size是K的小根堆,堆頂即當前第K小的數。輸入的數與堆頂元素比較,若比其大則堆頂變成當前第K+1大的數,故替換堆頂。
故此題利用兩個堆,一個小根堆,一個大根堆,小根堆儲存所有前K大的值,大根堆儲存所有其它的值,即小根堆中的值嚴格大於等於大根堆中的值,大根堆堆頂為K+1大的數。每次輸入元素放入大根堆中。K增加,則將原大根堆的根元素放入小根堆中。
3.兩序列和的前n小元素
- 給出兩個長度為n的有序表(非降序表)A和B, 在A和B中各任取一個相加, 可以得到n^2個和。
- 求這些和最小的n個。
- n<=10^5.
Solution
先考慮k路歸併問題
- 把k個有序表合併成一個有序表.
- 求前q個元素。
因為k個表都是有序的,故每個表第一個資料最小(以最小為例)。故比較每個表第一個資料,並輸出最小值。可把每個表第一個資料建堆維護,輸出最小後再在該值所在表中調取下個元素。
故此題可以把這些和看成n個有序表:
- A[1]+B[1] <= A[1]+B[2] <= A[1]+B[3] <=…
- A[2]+B[1] <= A[2]+B[2] <= A[2]+B[3] <=…
- …
- A[n]+B[1] <= A[n]+B[2] <= A[n]+B[3] <=…
4.n序列和的前n小元素
- 上一題的加強版
- 給出n個長度為n的有序表(非降序表)A1,A2,A3……An, 在其中各取一個相加, 可以得到n^n個和。
- 求這些和最小的n個。
- n<=750.
Solution
兩兩合併即可。
5.醜數
- 對於一給定的素數集合S = {p1, p2, …, pK}, 來考慮那些質因數全部屬於S 的數的集合。
- 這個集合包括:p1, p1p2, p1p1, 和p1p2p3 (還有其它)。
- 這是個對於一個輸入的S的醜數集合。
- 注意:我們不認為1是一個醜數。你的工作是對於輸入的集合S去尋找集合中的第N個醜數。
6.輪廓線
- 每一個建築物用一個三元組表示(L, H, R), 表示
左邊界, 高度和右邊界 - 輪廓線用X, Y, X, Y…這樣的交替式表示
- 右圖的輪廓線為: (1, 11, 3, 13, 9, 0, 12, 7, 16,
3, 19, 18, 22, 3, 23, 13, 29, 0) - 給N個建築,求輪廓線