--- title: 'Heap' disqus: hackmd --- [TOC] ## Heap Tree - 堆積樹 * $Heap$ 是一顆 $Complete\ Binay\ Tree$ * 最適合當作 $Piority\ Queue$ * 所有 $Parent$ 皆 $\geq$ 或 $\leq$ $Child$ * $Root$ 具有最 大/小 值 * $Insertion$: 1. 依順序放置 $Node$ 2. 若不符合與 $Parent$ 定義 3. 向上與 $Parent$ 交換直到 $Root$ * $Deleteion:$ 1. 刪除 $Root\ Data$ 2. 將 $Last\ Node\ Data$ 置入 $Root$ 並移除 $Last\ Node$ 3. 若不符合與 $Child$ 定義則向下交換直到 $Leaf$ * $Max Heap$ : 將最大的 Child 向上移動 * $Min Heap$ :將最小的 Child 向上移動 --- ### Max Heap - 最大堆積 * Parent >= Child * Root 最大 ```graphviz digraph graphname{ 1 [label="100"] 2 [label="19"] 3 [label="36"] 4 [label="17"] 5 [label="3"] 6 [label="25"] 7 [label="1"] 8 [label="2"] 9 [label="7"] 1->2 1->3 2->4 2->5 3->6 3->7 4->8 4->9 } ``` --- #### **Max Heap Insertion - 插入** 1. 根據 Complete Binary Tree 特性,依序新增節點 5 ```graphviz digraph graphname{ 1 [label="100"] 2 [label="19"] 3 [label="36"] 4 [label="17"] 5 [label="3"] 6 [label="25"] 7 [label="1"] 8 [label="2"] 9 [label="7"] 10 [label="5" color=red] 1->2 1->3 2->4 2->5 3->6 3->7 4->8 4->9 5->10 } ``` 2. Parent 3 < Child 5 ( 不符合 Parent >= Child ) 進行交換 ```graphviz digraph graphname{ 1 [label="100"] 2 [label="19"] 3 [label="36"] 4 [label="17"] 5 [label="5" color=red] 6 [label="25"] 7 [label="1"] 8 [label="2"] 9 [label="7"] 10 [label="3"] 1->2 1->3 2->4 2->5 3->6 3->7 4->8 4->9 5->10 } ``` 3. Child Null 結束 :::warning **Time Complexity :** $Worst\ case$ 為從 $Leaf$ 移動到 $Root = Height = \left \lceil log_2(n+1) \right \rceil ≒ O( log_2n )$ ::: --- #### **Max Heap Deleteion - 刪除** 1. 承上例,將 Root Data 移除,並置入 Last Node Data 3,再移除 Last Node ```graphviz digraph graphname{ 1 [label="3" color=red] 2 [label="19"] 3 [label="36"] 4 [label="17"] 5 [label="5"] 6 [label="25"] 7 [label="1"] 8 [label="2"] 9 [label="7"] 1->2 1->3 2->4 2->5 3->6 3->7 4->8 4->9 } ``` 2. 判斷他的左兒子:Parent 3 < Child 19 ( 不符合 Parent >= Child ) 3. 判斷 Child:19 < 36 , 比較大的 36 與 3 交換 ```graphviz digraph graphname{ 1 [label="36"] 2 [label="19"] 3 [label="3" color=red] 4 [label="17"] 5 [label="5"] 6 [label="25"] 7 [label="1"] 8 [label="2"] 9 [label="7"] 1->2 1->3 2->4 2->5 3->6 3->7 4->8 4->9 } ``` 4. Parent 3 < Child 25 ( 不符合 Parent >= Child ) 5. 25 與 3 交換 ```graphviz digraph graphname{ 1 [label="36"] 2 [label="19"] 3 [label="25"] 4 [label="17"] 5 [label="5"] 6 [label="3" color=red] 7 [label="1"] 8 [label="2"] 9 [label="7"] 1->2 1->3 2->4 2->5 3->6 3->7 4->8 4->9 } ``` 6. Child Null 結束 :::warning **Time Complexity:** $Worst\ case$ 為從 $Leaf$ 移動到 $Root = Height = \left \lceil log_2(n+1) \right \rceil ≒ O( log_2n )$ 因為每一次的判斷非大即小,相當於不停將可能的值乘上 $\frac{1}{2}$ 因此不論向上、向下的結果都是一樣的 ::: --- ### Min Heap - 最小堆積 * Parent <= Child * Root 最小 ```graphviz digraph graphname{ 1 [label="1"] 2 [label="2"] 3 [label="3"] 4 [label="17"] 5 [label="19"] 6 [label="36"] 7 [label="7"] 8 [label="25"] 9 [label="100"] 1->2 1->3 2->4 2->5 3->6 3->7 4->8 4->9 } ``` --- #### **Min Heap Insert - 插入** 1. 根據 Complete Binary Tree 特性,依序新增節點 5 ```graphviz digraph graphname{ 1 [label="1"] 2 [label="2"] 3 [label="3"] 4 [label="17"] 5 [label="19"] 6 [label="36"] 7 [label="7"] 8 [label="25"] 9 [label="100"] 10 [label="5" color=red] 1->2 1->3 2->4 2->5 3->6 3->7 4->8 4->9 5->10 } ``` 2.Parent 19 > Child 5 ( 不符合 Parent <= Child ) 進行交換 ```graphviz digraph graphname{ 1 [label="1"] 2 [label="2"] 3 [label="3"] 4 [label="17"] 5 [label="5" color=red] 6 [label="36"] 7 [label="7"] 8 [label="25"] 9 [label="100"] 10 [label="19"] 1->2 1->3 2->4 2->5 3->6 3->7 4->8 4->9 5->10 } ``` 3.Parent 2 < Child 5 ( 符合 Parent <= Child ) 4.結束 :::warning **Time Complexity:** 同 $Max\ Heap = O(log_2n)$ ::: --- #### **Min Heap Delete - 刪除** 1. 承上例,將 Root 移除,置入 Last Node 19 ```graphviz digraph graphname{ 1 [label="19" color=red] 2 [label="2"] 3 [label="3"] 4 [label="17"] 5 [label="5"] 6 [label="36"] 7 [label="7"] 8 [label="25"] 9 [label="100"] 1->2 1->3 2->4 2->5 3->6 3->7 4->8 4->9 } ``` 2. Parent 19 > Child ( 不符合 Parent <= Child ) 3. 判斷 Child:2 < 3 , 19 與 2 交換 ```graphviz digraph graphname{ 1 [label="2"] 2 [label="19" color=red] 3 [label="3"] 4 [label="17"] 5 [label="5"] 6 [label="36"] 7 [label="7"] 8 [label="25"] 9 [label="100"] 1->2 1->3 2->4 2->5 3->6 3->7 4->8 4->9 } ``` 4. Parent 19 > Child ( 不符合 Parent <= Child ) 5. 判斷 Child:5 < 17 , 19 與 5 交換 ```graphviz digraph graphname{ 1 [label="2"] 2 [label="5"] 3 [label="3"] 4 [label="17"] 5 [label="19" color=red] 6 [label="36"] 7 [label="7"] 8 [label="25"] 9 [label="100"] 1->2 1->3 2->4 2->5 3->6 3->7 4->8 4->9 } ``` 6. Child Null 結束 :::warning **Time Complexity:** 同 $Max\ Heap = O(log_2n)$ ::: --- ### Build Heap - 建置堆積 > [color=#52d356]**Top Down - 由上而下** > 1. 依序使用 $Insertion$ 從 Root (Top) 進行建置 > 2. 因為 $Insertion$ 中會保持 Heap 性質,所以不需要進行調整 * 給定一筆資料:`2 ,5 ,8 ,3 ,4 ,7 ,1 ,9 ,12` 建立 Max Heap 1. Node 為 Null , Insert 2 ```graphviz digraph graphname{ 1 [label="2" color=red] } ``` 2. Insert 5 ```graphviz digraph graphname{ 1 [label="2"] 2 [label="5" color=red] 1->2 } ``` 3. Parent 2 < Child 5 ( 不符合 Parent >= Child ) 4. 交換 ```graphviz digraph graphname{ 1 [label="5" color=red] 2 [label="2"] 1->2 } ``` 5. Insert 8 ```graphviz digraph graphname{ 1 [label="5" ] 2 [label="2"] 3 [label="8"color=red] 1->2 1->3 } ``` 6. Parent 5 < Child 8 ( 不符合 Parent >= Child ) 7. 交換 ```graphviz digraph graphname{ 1 [label="8" color=red] 2 [label="2"] 3 [label="5"] 1->2 1->3 } ``` 8. 以此類推 ```graphviz digraph graphname{ 1 [label="12"] 2 [label="9"] 3 [label="7"] 4 [label="8"] 5 [label="3"] 6 [label="5"] 7 [label="1"] 8 [label="2"] 9 [label="4" color=red] 1->2 1->3 2->4 2->5 3->6 3->7 4->8 4->9 } ``` :::warning **Time Complexity:** $Insert\ 1$ 個 Node 所需時間為 $O(log_2^n)$ $Insert\ n$ 個 Node 所需時間為 $log_21 + log_22 + ... + log_2n ≒ O(nlog_2n)$ ::: --- > [color=#52d356]**Bottom Up - 由下而上** > 1. 先將 n 個 Data 先依序建立成 Complete Binary Tree > 2. 再依序從 Last Parent 往 Root 方向調整每一顆子樹 * 給定一筆資料:`2 ,5 ,8 ,3 ,4 ,7 ,1 ,9 ,12` 建立 Max Heap 1. 依序將 Data 放進 Binary Tree ( 在程式使用時,此 Tree 為 Input , 所以在 Time Complexity 中不須計算此步驟) ```graphviz digraph graphname{ 1 [label="2"] 2 [label="5"] 3 [label="8"] 4 [label="3"] 5 [label="4"] 6 [label="7"] 7 [label="1"] 8 [label="9"] 9 [label="12"] 1->2 1->3 2->4 2->5 3->6 3->7 4->8 4->9 } ``` 2. Last Parent 為 3 ,我們以 3 作為 Root 進行調整 ```graphviz digraph graphname{ 1 [label="2"] 2 [label="5"] 3 [label="8"] 4 [label="3" color=blue fontcolor=red] 5 [label="4"] 6 [label="7"] 7 [label="1"] 8 [label="9" color=blue] 9 [label="12" color=blue] 1->2 1->3 2->4 2->5 3->6 3->7 4->8 [color=blue] 4->9 [color=blue] } ``` 3. Parent 3 < Child (不符合 Parent >= Child) 4. 判斷 Child : 9 < 12 , 將 3 與 12 交換 ```graphviz digraph graphname{ 1 [label="2"] 2 [label="5"] 3 [label="8" ] 4 [label="12" color=blue] 5 [label="4"] 6 [label="7"] 7 [label="1"] 8 [label="9" color=blue] 9 [label="3" color=blue fontcolor=red] 1->2 1->3 2->4 2->5 3->6 3->7 4->8 [color=blue] 4->9 [color=blue] } ``` 5. 下一個 Last Parent 為 8 ,我們以 8 作為 Root 進行調整 ```graphviz digraph graphname{ 1 [label="2"] 2 [label="5"] 3 [label="8" color=blue fontcolor=red] 4 [label="12"] 5 [label="4"] 6 [label="7" color=blue] 7 [label="1" color=blue] 8 [label="9"] 9 [label="3"] 1->2 1->3 2->4 2->5 3->6 [color=blue] 3->7 [color=blue] 4->8 4->9 } ``` 6. Parent 8 >= Child (符合定義) 7. 下一個 Last Parent 為 5 ,我們以 5 作為 Root 進行調整 ```graphviz digraph graphname{ 1 [label="2"] 2 [label="5" color=blue fontcolor=red] 3 [label="8" ] 4 [label="12" color=blue] 5 [label="4" color=blue] 6 [label="7" ] 7 [label="1"] 8 [label="9" color=blue] 9 [label="3" color=blue] 1->2 1->3 2->4 [color=blue] 2->5 [color=blue] 3->6 3->7 4->8 [color=blue] 4->9 [color=blue] } ``` 8. Parent 5 < Child 12 (不符合 Parent >= Child) 9. 進行交換 ```graphviz digraph graphname{ 1 [label="2"] 2 [label="12" color=blue ] 3 [label="8" ] 4 [label="5" color=blue fontcolor=red] 5 [label="4" color=blue] 6 [label="7" ] 7 [label="1"] 8 [label="9" color=blue] 9 [label="3" color=blue] 1->2 1->3 2->4 [color=blue] 2->5 [color=blue] 3->6 3->7 4->8 [color=blue] 4->9 [color=blue] } ``` 10. Parent 5 < Child 9 (不符合 Parent >= Child) 11. 進行交換 ```graphviz digraph graphname{ 1 [label="2"] 2 [label="12" color=blue ] 3 [label="8" ] 4 [label="9" color=blue ] 5 [label="4" color=blue] 6 [label="7" ] 7 [label="1"] 8 [label="5" color=blue fontcolor=red] 9 [label="3" color=blue] 1->2 1->3 2->4 [color=blue] 2->5 [color=blue] 3->6 3->7 4->8 [color=blue] 4->9 [color=blue] } ``` 12. 下一個 Last Parent 為 2 ,我們以 2 作為 Root 進行調整 ```graphviz digraph graphname{ 1 [label="2" color=blue fontcolor=red] 2 [label="12" color=blue ] 3 [label="8" color=blue] 4 [label="9" color=blue ] 5 [label="4" color=blue] 6 [label="7" color=blue] 7 [label="1"color=blue] 8 [label="5" color=blue ] 9 [label="3" color=blue] 1->2 [color=blue] 1->3 [color=blue] 2->4 [color=blue] 2->5 [color=blue] 3->6 [color=blue] 3->7 [color=blue] 4->8 [color=blue] 4->9 [color=blue] } ``` 13. Parent 2 < Child (不符合 Parent >= Child) 14. 判斷 Child : 12 > 8 , 將 2 與 12 交換 ```graphviz digraph graphname{ 1 [label="12" color=blue ] 2 [label="2" color=blue fontcolor=red] 3 [label="8" color=blue] 4 [label="9" color=blue ] 5 [label="4" color=blue] 6 [label="7" color=blue] 7 [label="1"color=blue] 8 [label="5" color=blue ] 9 [label="3" color=blue] 1->2 [color=blue] 1->3 [color=blue] 2->4 [color=blue] 2->5 [color=blue] 3->6 [color=blue] 3->7 [color=blue] 4->8 [color=blue] 4->9 [color=blue] } ``` 14. Parent 2 < Child (不符合 Parent >= Child) 15. 判斷 Child : 9 > 4 , 將 2 與 9 交換 ```graphviz digraph graphname{ 1 [label="12" color=blue ] 2 [label="9" color=blue ] 3 [label="8" color=blue] 4 [label="2" color=blue fontcolor=red] 5 [label="4" color=blue] 6 [label="7" color=blue] 7 [label="1"color=blue] 8 [label="5" color=blue ] 9 [label="3" color=blue] 1->2 [color=blue] 1->3 [color=blue] 2->4 [color=blue] 2->5 [color=blue] 3->6 [color=blue] 3->7 [color=blue] 4->8 [color=blue] 4->9 [color=blue] } ``` 14. Parent 2 < Child (不符合 Parent >= Child) 15. 判斷 Child : 5 > 3 , 將 2 與 5 交換 ```graphviz digraph graphname{ 1 [label="12" color=blue ] 2 [label="9" color=blue ] 3 [label="8" color=blue] 4 [label="5" color=blue ] 5 [label="4" color=blue] 6 [label="7" color=blue] 7 [label="1"color=blue] 8 [label="2" color=blue fontcolor=red] 9 [label="3" color=blue] 1->2 [color=blue] 1->3 [color=blue] 2->4 [color=blue] 2->5 [color=blue] 3->6 [color=blue] 3->7 [color=blue] 4->8 [color=blue] 4->9 [color=blue] } ``` 16.無 Next Last Parent 結束 ```graphviz digraph graphname{ 1 [label="12"] 2 [label="9"] 3 [label="8"] 4 [label="5"] 5 [label="4"] 6 [label="7"] 7 [label="1"] 8 [label="2"] 9 [label="3"] 1->2 1->3 2->4 2->5 3->6 3->7 4->8 4->9 } ``` :::danger 眼尖的同學可能會發現 同樣一筆次序的資料,**Bottom Up** 及 **Top Down** 的結果並不一致 因此我們可以推論 **Heap Binary Tree** 不具唯一性 ::: * 喝個水休息一下,以下我們將花點時間討論 **Bottom Up** 的 **演算法** 藉由此圖初步說明 **[Bottom Up - Analysis Diagram](https://i.imgur.com/fK8A7lu.png)** ![](https://i.imgur.com/fK8A7lu.png) :::success * **Bottom Up 演算法分析** 依照演算法的步驟,我們會先從 Last Parent 開始進行整理 也就是圖中 Level 4 藍色三角形那一列 若藍色三角形裡發生 Parent 與 Child 定義不符合時 我們需要調整的最多步驟為:5 - 4 = 1步,也就是 Leaf (Level 5) 與該 Level 的距離 * 由此可得 1. Level 4 裡其中一個藍色三角形到 Leaf 的移動成本為 1 2. Level 4 裡有 2^4-1^ = 8 個藍色三角形 ( 也就是以 Level 4 的 Node 為 Root 的子樹有 8 顆 ) 3. 藍色三角形數量 $*$ 藍色三角形最大移動成本 = 藍色三角形移動總成本 4. 也就是圖中的 8 * 1 = 8 <br> 依序還有 綠色、橘色、紅色 三角形需要計算相加 即可得整棵樹的最大移動成本 由此整理出以下數學公式 * **Def:樹高起始點 = 1,樹葉高度 = k,Last Parent Level = i** 1. $\displaystyle\sum_{樹高起點}^{樹葉高度}(該\ Level\ 最多\ Node\ 數*與\ Leaf\ 的距離)$ 2. $\displaystyle\sum_{i=1}^{k}(2^{i-1}*(k-i))$ <br> 大家應該有發現紫色三角形,也就是 Last Parent 到了 Leaf 時 因為已經沒有 Child ,所以不管 Leaf 有多少,移動成本皆為 0 代表我們並不用在 [Summation](https://en.wikipedia.org/wiki/Summation) 裡將此納入計算 3. 得 $\displaystyle\sum_{i=1}^{k-1}(2^{i-1}*(k-i))$ 要注意的是,在公式裡我們從 Level 1 開始進行計算 是假設過程中的每一步都是最大成本,所以次序並不那麼重要 而在實際操作時必須確實地從 Last Parent 逐漸往上調整 否則將無法做出符合 Max/Min Heap 的 Tree 4. 當然你也可以改變一下 Summation: $\displaystyle\sum_{i=1}^{k-1}(2^{k-i-1}*i)$ ::: :::warning **Time Complexity:** 為了求出 **Big-O** 我們將 $\displaystyle\sum_{i=1}^{k-1}(2^{i-1}*(k-i))$ 展開、嘗試整理 <br> * Let $Cost = 2^0*(k-1)+2^1*(k-2)+...+2^{k-3}*(2)+2^{k-2}*(1)$ (以下用點小技巧) * $2 * Cost = 2^1*(k-1)+2^2*(k-2)+...+2^{k-3}*(3)+2^{k-2}*(2)+2^{k-1}*(1)$ ::: * 我們將 **$(2*Cost) - (Cost)$** ![](https://i.imgur.com/15zSa3M.png) :::warning * 得 $Cost = -2^0*(k-1)+2^1+2^2+...+2^{k-2}+2^{k-1}$<br> 我們發現了一等比數列:$2^1+2^2+...+2^{k-2}+2^{k-1}$<br> 根據等比級數求和公式:$\dfrac{公比^{最大項次方+1}-首項}{公比-1}$ 得:$\dfrac{2^k-2^1}{2-1}$ = $2^k-2$ <br> 整理 $(2^k-2)-(2^0*(k-1))=2^k-k-1$ :+1: <br> 到此你應該會感到非常興奮,如果沒有的話代表你迷失在處理公式的迷宮裡 回顧一下我們到底在做甚麼事情,以下是我們的初衷 1. Level 4 裡其中一個藍色三角形到 Leaf 的移動成本為 1 2. Level 4 裡有 2^4-1^ = 8 個藍色三角形 ( 也就是以 Level 4 的 Node 為 Root 的子樹有 8 顆 ) 3. 藍色三角形數量 * 藍色三角形最大移動成本 = 藍色三角形移動總成本 4. 也就是圖中的 8 * 1 = 8 依序還有 綠色、橘色、紅色 三角形需要計算相加 即可得整棵樹的最大移動成本 由此整理出以下數學公式 * 沒錯,我們從好幾串的文字敘述精簡到了 $2^k-k-1$ 這樣的公式 那麼... $k$ 是甚麼? $k$ 是高度你還記得吧! 代表只要將高度代入就可以省去三角形圖的繁瑣計算 圖中的 Level = $5$,代入 $2^k-k-1$<br> **=> $2^5-5-1$ = 三角形圖裡各層最大成本合 = $4+6+8+8=26$**<br> 這就是 **數學與演算法之美** 於是你可以在自己的函式庫裡新增一個功能就叫做: int 二元堆積樹的最大成本(int 樹高){ return pow(2,樹高)-樹高-1 } * 是不是很棒呢,希望可以增加你對數學的熱忱<br> 回歸正題,我們要求出 $2^k-k-1$ 的 **Big-O**<br> 設 $n = 2^k$ , 即 $k=log_2n$<br> <br> 1. 將 $k=log_2n$ 代入 $2^k-k-1$ 3. $2^{log_2n}-log_2n-1$ 4. 根據對數性質:$2^{log_2n}$ = $n^{log_22}$ 5. $n^{log_22}-log_2n-1$ 6. $n-log_2n-1$ 7. $n-log_2n-1$ ≦ $c*n=n$ (設$c$為常數=$1$)<br> <br> 得 **Bottom up 之 Time Complexity :O(n)** ::: > [color=#]在經由一連串的分解後我們終於得到了 Bottom Up 的 Time Complexity > > 且值得慶幸的是,該 Time Complexity 比 Top Down **O(nlogn)** 的還要快上很多 > > 所以看似簡單易懂的演算法,不一定會比艱澀難懂的還要快呢! > > [time=Tue, May 7, 2019 12:50 AM] --- ### 刪除 插入 建置 Bottom up之調整 Code To be continued ###### tags: `Data Structure`