äºåï¼æ大ï¼å æ¯ä¸ä¸ªç»´ææ大å å±æ§çå®å ¨äºåæ ã
äºåå æ¯å®ç°é«æä¼å éåï¼PQï¼æ½è±¡æ°æ®ç±»åï¼ADTï¼çä¸ç§å¯è½çæ°æ®ç»æãå¨PQä¸ï¼æ¯ä¸ªå ç´ é½æä¸ä¸ªâä¼å 级âï¼ä¼å 级è¾é«çå ç´ å¨ä¼å 级è¾ä½çå ç´ ä¹å被æå¡ï¼å¹³å±å¯ä»¥ç®åå°éæ解å³ï¼æè æç §æ®ééåçå è¿å åºï¼FIFOï¼è§å解å³ï¼ãå°è¯ç¹å» ï¼æ¥çä¸é¢éæºäºåå æåæ大å¼ç示ä¾å¨ç»ã
为äºéä¸è®¨è®ºèå´ï¼è¿ä¸ªå¯è§åå±ç¤ºäºä¸ä¸ªå 许éå¤çæ´æ°äºåæ大å ãæ¥çè¿ä¸ªï¼äºè§£å¦ä½è½»æ¾è½¬æ¢ä¸ºäºåæå°å ãé常ï¼ä»»ä½å¯ä»¥æ¯è¾çå ¶ä»å¯¹è±¡é½å¯ä»¥åå¨å¨äºåæ大å ä¸ï¼ä¾å¦ï¼æµ®ç¹æ°çäºåæ大å çã
å®å ¨äºåæ ï¼äºåæ ä¸çæ¯ä¸å±ï¼é¤äºå¯è½çæå/æä½å±ï¼é½è¢«å®å ¨å¡«æ»¡ï¼ä¸æåä¸å±çææ顶ç¹å°½å¯è½å°é å·¦ã
äºåæ大å å±æ§ï¼æ¯ä¸ªé¡¶ç¹çç¶é¡¶ç¹ï¼é¤äºæ ¹ï¼å å«çå¼å¤§äºï¼æçäº ââ æ们ç°å¨å 许éå¤ï¼è¯¥é¡¶ç¹çå¼ãè¿æ¯ä»¥ä¸çæ¿ä»£å®ä¹æ´å®¹æéªè¯ï¼ä¸ä¸ªé¡¶ç¹çå¼ï¼é¤äºå¶å/å¶åï¼å¿ 须大äºï¼æçäºï¼å ¶ä¸ä¸ªï¼æ两个ï¼åèç¹çå¼ã
ä¼å éå (PQ) æ½è±¡æ°æ®ç±»å (ADT) ä¸æ®ééå ADT 类似ï¼ä½æ以ä¸ä¸¤ä¸ªä¸»è¦æä½ï¼
讨论ï¼ä¸äº PQ ADT å¨ PQ ä¸æé«ä¼å 级ï¼é®ï¼å¹¶åçæ åµä¸ï¼ä¼æ¢å¤å°æ®ééåçå è¿å åº (FIFO) è¡ä¸ºãä¿è¯å¨æé«ä¼å 级ï¼é®ï¼ç¸ççæ åµä¸ç稳å®æ§æ¯å¦ä½¿ PQ ADT æ´é¾å®ç°ï¼
æ³è±¡ä¸ä¸ï¼ä½ æ¯ä¸åå¨æºåºæ§å¶å¡Tå·¥ä½ç空ä¸äº¤é管å¶å (ATC)ãä½ å·²ç»å®æé£æºX/Yåå«å¨æ¥ä¸æ¥ç3/6åéå éè½ã两æ¶é£æºççæé½è¶³å¤é£è¡è³å°æ¥ä¸æ¥ç15åéï¼èä¸é½ç¦»ä½ çæºåºåªæ2åéçè·¯ç¨ãä½ è§å¯å°ä½ çæºåºè·éç®åæ¯æ¸ æ°çã
å¦æä½ ä¸ç¥éï¼é£æºå¯ä»¥è¢«æ示å¨æºåºéè¿ä¿æé£è¡æ¨¡å¼ï¼ç´å°æå®çéè½æ¶é´ã
ä½ å¿ é¡»åå ç°åºè®²åº§æè½å¼æ¸ æ¥æ¥ä¸æ¥ä¼åçä»ä¹...
å°åä½ æä¾ä¸¤ä¸ªé项ï¼ä½ éè¦ååºå³å®ï¼
å¦æè¿ä¸¤ä¸ªé项é½ä¸åçï¼é£å°±ä»ä¹é½ä¸åã
é¤äºä½ åæçå°çå ³äºATCï¼ä» å¨ç°åºè®²åº§ä¸ï¼çå 容å¤ï¼ä¼å éå ADTå¨ç°å®çæ´»ä¸è¿æå ç§æ½å¨çç¨éã
讨论ï¼ä½ è½æåºå ä¸ªå ¶ä»éè¦ä¼å éåçç°å®çæ´»æ åµåï¼
We are able to implement this PQ ADT using (circular) Array or Linked List but we will have slow (i.e., in O(N)) Enqueue or Dequeue operation.
Discussion: Why?
Now, let's view the visualisation of a (random) Binary (Max) Heap above. You should see a complete binary tree and all vertices except the root satisfy the Max Heap property (A[parent(i)] ≥ A[i]). Duplicate integer keys may appear (note that the stability of equal keys is not guaranteed).
You can
between the visually more intuitive complete binary tree form or the compact array based implementation of a Binary (Max) Heap.Quiz: Based on this Binary (Max) Heap property, where will the largest integer be located?
å®å ¨äºåæ å¯ä»¥ä½ä¸ºç´§åæ°ç»Aææå°åå¨ï¼å 为å®å ¨äºåæ ç顶ç¹/ç´§åæ°ç»çå ç´ ä¹é´æ²¡æé´éã为äºç®åä¸é¢ç导èªæä½ï¼æ们使ç¨åºäº1çæ°ç»ãVisuAlgoå¨æ¯ä¸ªé¡¶ç¹ä¸æ¹ä»¥çº¢è²æ ç¾æ¾ç¤ºæ¯ä¸ªé¡¶ç¹çç´¢å¼ãæç §ä»1å°Nçæåºé¡ºåºé 读è¿äºç´¢å¼ï¼ç¶åä½ ä¼çå°ä»ä¸å°ä¸ï¼ä»å·¦å°å³çå®å ¨äºåæ ç顶ç¹ã为äºå¸®å©ä½ ç解è¿ä¸ç¹ï¼ å 次ã
è¿æ ·ï¼æ们å¯ä»¥ä½¿ç¨ç®åçç´¢å¼æä½ï¼åå©ä½ç§»æä½ç帮å©ï¼å®ç°åºæ¬çäºåæ éåæä½ï¼
ä¸ä¸æ示ï¼å°è¯å¨ä¸¤ä¸ªæµè§å¨çªå£ä¸æå¼ä¸¤ä¸ªVisuAlgoçå¯æ¬ãå°è¯å¨ä¸¤ç§ä¸åç模å¼ä¸å¯è§ååä¸ä¸ªäºåæ大å ï¼å¹¶è¿è¡æ¯è¾ã
å¨è¿ä¸ªå¯è§åä¸ï¼ä½ å¯ä»¥æ§è¡å 个äºåï¼æ大ï¼å æä½ï¼
è¿æä¸äºå ¶ä»å¯è½çäºåï¼æ大ï¼å æä½ï¼ä½æ¯æ们ç®åå¨æ个NUS模åä¸åºäºæå¦åå 并æªè¯¦ç»è¯´æã
Insert(v): å½æ们å¨å¾æ大äºåå æå ¥å ç´ v çæ¶å æ们åªè½æå ¥å°æåä¸ä¸ªå ç´ N + 1 æ¥ç»´æä¸ä¸ªç´§åçæ°ç» = å®æ´äºåæ ç¹æ§ãç¶è æ大å çç¹æ§ä»ç¶å¯è½æ²¡è¢«æ»¡è¶³ãè¿æ¶æ们ä»æå ¥ç¹å¾ä¸å»ä¿®å¤æ大å ç¹æ§ï¼å¦æéè¦çè¯ï¼ç´å°æ´ä¸ªå 符åæ大å ç¹æ§ãç°å¨ç¹
å 次æ¥æå ¥å 个éæºç v è¿å»ç°å¨æ¾ç¤ºçæ大äºåå ãè¿ä¸ªåä¸èµ°å¹¶ä¸ä¿®å¤æ大å ç¹æ§çæä½æ²¡æå ¬è®¤çååãæ们å«å® ShiftUp ï¼ä¸ç§»ï¼ä½æ¯è¿æ人å«å® BubbleUp æè IncreaseKey æä½.
Insert(v) çæ¶é´å¤æåº¦æ¯ O(log N)ã
讨论ï¼ä½ ç解è¿ä¸ªæ¨è®ºåï¼
ExtractMax(): æå并å é¤æ大äºåå ä¸çæ大å ç´ ï¼æ ¹ï¼çæä½éè¦ä¸ä¸ªå¨å ä¸çå ç´ æ¥æ¿æ¢æ ¹ï¼ä¸ç¶æ大å ï¼ä¸ä¸ªå®æ´çäºåæ ï¼å¨ä¸æä¸ç'æâï¼ä¼åæ两个æå¼çåæ ï¼ä¸æä¸ä¸¤ä¸ª'æ¨âï¼ãè¿åæ ·ä¹æ¯è¿ä¸ªå ç´ å¿ é¡»æ¯æåä¸ä¸ªå ç´ Nçåå ï¼ç»´æç´§åæ°ç» = å®æ´äºåæ æ§è´¨ã
å 为æ们è¦ææ大äºåå ä¸çä¸ä¸ªå¶é¡¶ç¹æåææ ¹é¡¶ç¹ï¼å®å¾å¯è½æ¯ä¼è¿èæ大å ç¹æ§ãExtractMax() è¿æ¶ä»æ ¹å¾ä¸æ¯è¾å½åå¼åå®ç两个åå ç´ çå¼ï¼å¦æå¿ è¦çè¯ï¼æ¥ä¿®å¤æ大äºåå ç¹æ§ãç°å¨å¨å½åæ¾ç¤ºçæ大äºåå ä¸è¯è¯
è¿ä¸ªåä¸èµ°å¹¶ä¿®å¤æ大å ç¹æ§çæä½æ²¡æå ¬è®¤çååãæ们å«å® ShiftDown ï¼ä¸ç§»ï¼ä½æ¯è¿æ人å«å® BubbleDown æè Heapify æä½ã.
ExtractMax() çæ¶é´å¤æåº¦æ¯ O(log N)ã
讨论ï¼ä½ ç解è¿ä¸ªæ¨è®ºåï¼
å°ç®å为æ¢ï¼æ们æä¸ä¸ªæ°æ®ç»æï¼å¯ä»¥ææå°å®ç°ä¼å éå (PQ) ADTç两个主è¦æä½ï¼
ç¶èï¼æ们å¯ä»¥ç¨äºåå åæ´å¤çæä½ã
Create(A): ç¨å«æ N 个éå·åéçæ°ç» A ä»ä¸ä¸ªç©ºçæ大äºåå å¼å§å建ä¸ä¸ªåæ³çæ大äºåå
æ两ç§æ¹æ³å®æå建æä½ä¸ä¸ªç®åä½æ¯æ ¢ -> O(N log N) å¦ä¸ä¸ªç¨æ´å¤æçææ¯ä½æ¯å¿« -> O(N).
æä½ ä¸æï¼å¨ä¸¤ä¸ªæµè§å¨éé¢åæ¶æå¼ VisuAlgoãç¨æå·®æ åµ 'å·²æåºä¾å' å»æ§è¡ä¸åçæ¬ç Create(A)ï¼ä½ ä¼çå°å¯æçå·®è·ã
Create(A) - O(N log N)ï¼ç®åå°æå ¥ï¼å³ï¼éè¿è°ç¨ Insert(v) æä½ï¼è¾å ¥æ°ç»ä¸çææ N 个æ´æ°å°ä¸ä¸ªæå为空çäºåæ大å ä¸ã
åæï¼è¿ä¸ªæä½æ¾ç¶æ¯ O(N log N)ï¼å 为æ们è°ç¨ O(log N) Insert(v) æä½ N 次ã让æ们æ¥çç 'æåºç¤ºä¾'ï¼è¿æ¯è¿ä¸ªæä½çä¸ä¸ªé¾ç¹ï¼ç°å¨è¯è¯
ï¼æ们å±ç¤ºäºä¸ä¸ªæ¡ä¾ï¼å ¶ä¸ A = [1,2,3,4,5,6,7] -- 请èå¿çå¾ ï¼å 为è¿ä¸ªç¤ºä¾éè¦ä¸äºæ¶é´æ¥å®æï¼ãå¦ææ们æéå¢é¡ºåºå°å¼æå ¥å°ä¸ä¸ªæå为空çäºåæ大å ä¸ï¼é£ä¹æ¯æ¬¡æå ¥é½ä¼è§¦åä¸æ¡ä»æå ¥ç¹ï¼ä¸ä¸ªæ°çå¶åï¼åä¸å°æ ¹çè·¯å¾ãCreate(A) - O(N): This faster version of Create(A) operation was invented by Robert W. Floyd in 1964. It takes advantage of the fact that a compact array = complete binary tree and all leaves (i.e., half of the vertices â see the next slide) are Binary Max Heap by default. This operation then fixes Binary Max Heap property (if necessary) only from the last internal vertex back to the root.
Analysis: A loose analysis gives another O(N/2 log N) = O(N log N) complexity but it is actually just O(2*N) = O(N) â details here. Now try the on the same input array A = [1,2,3,4,5,6,7] and see that on the same hard case as with the previous slide (but not the one that generates maximum number of swaps — try 'Diagonal Entry' test case), this operation is far superior than the O(N log N) version.
ç®åè¯æ为ä»ä¹å¨æ大å ä¸ï¼éé¢N个å ç´ ï¼ä¸åçå ç´ é½æ¯å¶å ç´ ï¼ä¸ºäºæ¦æ¬æ§ï¼æ们å设Næ¯å¶æ°ï¼ï¼
å设æåä¸ä¸ªå¶å ç´ çåºå·æ¯ N, é£ä¹å®çç¶å ç´ çåºå·æ¯ i = N/2 (åå¿ è¿è课). å®çå·¦åå ç´ çåºå·æ¯ i+1, å¦æåå¨çè¯ (å®å ¶å®å¹¶ä¸åå¨), å°ä¼æ¯ 2*(i+1) = 2*(N/2+1) = N+2, è¿ä¸ªç»æè¶ è¿äº N (æåä¸ä¸ªå¶å ç´ ) æ以 i+1 ä¸å®æ¯ä¸ä¸ªæ²¡æåå ç´ çå¶å ç´ ãå 为äºåå çåºå·æ¯è¿ç»çï¼æ以åºå·s [i+1 = N/2+1, i+2 = N/2+2, ..., N], æè 说ä¸åç顶ç¹ï¼é½æ¯å¶å ç´ ã
å æåºå½æ°HeapSort()ï¼ John William Joseph Williams å¨1964å¹´åæäºå æåºç®æ³åè¿ä¸ªäºåå æ°æ®ç»æ. å æåºçæä½ ï¼åå®æ大äºåå å·²ç»å¨O(n)çæ¶é´å¤æ度å 建ç«ï¼é常ç®åãåªéè¦è°ç¨æåå 顶å½æ°ExtractMax() n次å³å¯ï¼æ¯æ¬¡çæ¶é´å¤æ度为O(log N). ç°å¨ä½ å¯ä»¥è¯ä¸ä¸å±å¹ä¸çè¿ä¸ªæ大äºåå
Quiz: In worst case scenario, HeapSort() is asymptotically faster than...
Merge Sort尽管 HeapSort() å¨æ好/å¹³å/æå æ åµä¸çæ¶é´å¤æ度é½æ¯ θ(N log N) ï¼å®ççæ¯æ好çç¨æ¯è¾å ç´ çæ¹æ³æ¥å®ç°çæåºç®æ³åï¼
讨论ç¹ï¼HeapSort()çç¼åæ§è½å¦ä½ï¼
å½ä½ å
¨é¨å¦ä¼äºçæ¶åï¼æ们éè¯·ä½ å¦ä¹ æ´é«çº§çç®æ³ï¼å®ä»¬ç¨ä¼å
éååºå±æ°æ®ç»æï¼ä¹ä¸ï¼ï¼ä¾å¦ Prim's MST algorithm, Dijkstra's SSSP algorithm, A* æç´¢ç®æ³(è¿ä¸å¨ VisuAlgoé), ä¸äºè´ªå©ªç®æ³ï¼çç.
Earlier, we have seen that we can create Binary Max Heap from a random array of size N elements in O(N) instead of O(N log N). Now, we will properly analyze this tighter bound.
First, we need to recall that the height of a full binary tree of size N is log2 N.
Second, we need to realise that the cost to run shiftDown(i) operation is not the gross upper bound O(log N), but O(h) where h is the height of the subtree rooted at i.
Third, there are ceil(N/2h+1) vertices at height h in a full binary tree.
On the example full binary tree above with N = 7 and h = 2, there are:
ceil(7/20+1) = 4 vertices: {44,35,26,17} at height h = 0,
ceil(7/21+1) = 2 vertices: {62,53} at height h = 1, and
ceil(7/22+1) = 1 vertex: {71} at height h = 2.
Cost of Create(A), the O(N) version is thus:
PS: If the formula is too complicated, a modern student can also use WolframAlpha instead.
The faster O(N) Create Max Heap from a random array of N elements is important for getting a faster solution if we only need top K elements out of N items, i.e., PartialSort().
After O(N) Create Max Heap, we can then call the O(log N) ExtractMax() operation K times to get the top K largest elements in the Binary (Max) Heap. Now try
on the currently displayed Binary (Max) Heap.Analysis: PartialSort() clearly runs in O(N + K log N) — an output-sensitive algorithm where the time complexity depends on the output size K. This is faster than the lower-bound of O(N log N) if we fully sort the entire N elements when K < N.
If there are duplicate keys, the standard implementation of Binary Heap as shown in this visualization does not guarantee stability. For example, if we insert three copies of {7, 7, 7}, e.g., {7a, 7b, and 7c} (suffix a, b, c are there only for clarity), in that order, into an initially empty Binary (Max) Heap. Then, upon first extraction, the root (7a) will be extracted first and the last existing leaf (7c) will replaces 7a. As 7c and 7b (without the suffixes) are equal (7 and 7), there is no swap happening and thus the second extract max will take out 7c instead of 7b first — not stable.
If we really need to guarantee stability of equal elements, we probably need to attach different suffixes as shown earlier to make those identical elements to be unique again.
å¦æä½ æ£å¨å¯»æ¾äºåï¼æ大ï¼å çå®ç°æ¥å®é 模æä¼å éåï¼é£ä¹æ个好æ¶æ¯ã
C++ å Java å·²ç»æå ç½®çä¼å éåå®ç°ï¼å¾å¯è½ä½¿ç¨äºè¿ç§æ°æ®ç»æãå®ä»¬æ¯ C++ STL priority_queueï¼é»è®¤æ¯æ大ä¼å éåï¼å Java PriorityQueueï¼é»è®¤æ¯æå°ä¼å éåï¼ãç¶èï¼å ç½®çå®ç°å¯è½ä¸éåææå°æ§è¡ä¸äº PQ æ©å±æä½ï¼å¨æ个 NUS 课ç¨ä¸åºäºæå¦åå çç¥äºè¯¦ç»ä¿¡æ¯ï¼ã
Python ç heapq åå¨ï¼ä½å ¶æ§è½ç¸å½æ ¢ãOCaml 没æå ç½®çä¼å éåï¼ä½æ们å¯ä»¥ä½¿ç¨å°å¨ VisuAlgo çå ¶ä»æ¨¡åä¸æå°çå ¶ä»ä¸è¥¿ï¼çç¥è¯¦ç»ä¿¡æ¯çåå ä¸ä¸è¿°ç¸åï¼ã
éè¨ï¼å æåºå¯è½ç¨äº C++ STL ç®æ³ partial_sortã
ç¶èï¼è¿æ¯æ们ç BinaryHeapDemo.cpp | py | java çå®ç°ã
æ们è¿æä¸äºç¼ç¨é®é¢å¯ä»¥ç¨å°äºåå æ°æ®ç»æUVa 01203 - Argus å Kattis - numbertree.
è¯ç解å³å®ä»¬æ¥æé«ä½ 对è¿ä¸ªæ°æ®ç»æçç解ãä½ å¯ä»¥ç¨C++ STL priority_queue, Python heapqæè JAVA PriorityQueue å¦æå®ä»¬å¯ä»¥ç®åä½ ç解æ³