看圖輕鬆理解最小(大)堆

超人汪小建發表於2018-12-10

前言

推出一個新系列,《看圖輕鬆理解資料結構和演算法》,主要使用圖片來描述常見的資料結構和演算法,輕鬆閱讀並理解掌握。本系列包括各種堆、各種佇列、各種列表、各種樹、各種圖、各種排序等等幾十篇的樣子。

最小(大)堆

最小(大)堆是一顆完全二叉樹,該樹中的某個節點的值總是不大於(不小於)其左右子節點的值。可以通過下圖理解,另外,為什麼會使用陣列來儲存呢?因為利用完全二叉樹的性質,我們可以通過陣列來表示完全二叉樹(陣列下標與完全二叉樹節點存在對映關係,比如父節點可以通過Math.floor((index-1)/2)來獲取),從而簡化了實現及開銷,避免使用額外的指標來實現樹結構。

image

最小(大)堆性質

  • 樹根節點的值是所有堆節點值中最小(大)值。
  • 樹中每個節點的子樹也都是最小(大)堆。

最小(大)堆作用

最小(大)堆能保證堆頂元素為最小,而如果使用陣列無法達到該效果。陣列如果要訪問最小值則需要遍歷查詢最小值,時間複雜度至少O(n)。而最小堆訪問最小值時間複雜度為O(1),當然天底下沒有免費的午餐,我們需要做額外的工作去維護最小(大)堆的結構,這也是需要複雜度花銷的。

當然這也是最小(大)堆的優勢,通過動態維護使得最小值的獲取代價很小,實際上維護的時間複雜度為O(logN)。而陣列則無法做到如此,如果陣列想要維護順序性則需要的複雜度至少為O(N)。這樣來看最小(大)堆的優勢就凸現出來了。

插入操作

為避免冗長累贅,我們這裡只挑最小堆作為例子進行說明,最大堆的情況與最大堆相似。

現在分別插入4 7 2 5 6 1 0 3 8,使用一個陣列來儲存最小堆,為了幫助理解,陣列下方提供一個邏輯上的完全二叉樹的結構,兩者結合著更容易理解其中機制。首先插入4,

image

接著插入7,插入後檢測到樹符合最小堆要求,所以不改動。

image

繼續插入2,插入後檢測到不符合最小堆要求,父節點4大於右子節點2,

image

於是將它們對調。

image

繼續插入5,插入後檢測到不符合最小堆要求,父節點7大於左子節點5,

image

於是將它們對調。

image

繼續插入6,插入後檢測到樹符合最小堆要求,所以不改動。

image

繼續插入1,插入後檢測到不符合最小堆要求,父節點4大於左子節點1,

image

於是將它們對調,

image

對調後繼續檢測到不符合最小堆要求,父節點2大於右子節點1,

image

繼續將它們對調。

image

繼續插入0,插入後檢測到不符合最小堆要求,父節點2大於右子節點0,

image

於是將它們對調,

image

對調後繼續檢測到不符合最小堆要求,父節點1大於右子節點0,

image

繼續將它們對調。

image

繼續插入3,插入後檢測到不符合最小堆要求,父節點7大於左子節點3,

image

於是將它們對調,

image

對調後繼續檢測到不符合最小堆要求,父節點5大於左子節點3,

image

繼續將它們對調,然後符合最小堆要求,不必繼續往上對調。

image

繼續插入8,插入後檢測到樹符合最小堆要求,所以不改動。以上,完成所有元素的最小堆插入操作。

image

刪除操作

刪除操作其實就是刪除最小值,即最小堆樹中的根節點。主要是將樹中最後一個節點替換到被刪除的根節點,然後自頂向下遞迴調整使之符合最小堆要求。

刪除根節點0,然後將樹的最後一個節點8補到根節點上。

image

比較根節點的左右子節點,

image

因為右子節點1比較小,所以我們要進一步比較的是根節點8與右子節點1,

image

1小於8,於是對調。

image

繼續比較現在節點8的左右子節點,

image

因為右子節點2比較小,所以我們要進一步比較的是根節點8與右子節點2,

image

2小於8,於是對調。

image

至此,完成最小值刪除操作。

-------------推薦閱讀------------

我的開源專案彙總(機器&深度學習、NLP、網路IO、AIML、mysql協議、chatbot)

為什麼寫《Tomcat核心設計剖析》

我的2017文章彙總——機器學習篇

我的2017文章彙總——Java及中介軟體

我的2017文章彙總——深度學習篇

我的2017文章彙總——JDK原始碼篇

我的2017文章彙總——自然語言處理篇

我的2017文章彙總——Java併發篇


跟我交流,向我提問:

看圖輕鬆理解最小(大)堆

歡迎關注:

看圖輕鬆理解最小(大)堆

相關文章