1. 程式人生 > >[資料結構].堆(優先佇列)

[資料結構].堆(優先佇列)

堆(優先佇列)

堆的定義

一種樹形資料結構,分大根堆小根堆

大根堆(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.黑匣子

起先黑匣子為空。現在執行一系列命令,命令有兩類:

  1. ADD(x):把元素x放入黑匣子;
  2. 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個建築,求輪廓線Eg.5

7.Hoax or what(UVa11136)

引用

UNFINISHED