前言
推出一個新系列,《看圖輕鬆理解資料結構和演算法》,主要使用圖片來描述常見的資料結構和演算法,輕鬆閱讀並理解掌握。本系列包括各種堆、各種佇列、各種列表、各種樹、各種圖、各種排序等等幾十篇的樣子。
最小(大)堆
最小(大)堆是一顆完全二叉樹,該樹中的某個節點的值總是不大於(不小於)其左右子節點的值。可以通過下圖理解,另外,為什麼會使用陣列來儲存呢?因為利用完全二叉樹的性質,我們可以通過陣列來表示完全二叉樹(陣列下標與完全二叉樹節點存在對映關係,比如父節點可以通過Math.floor((index-1)/2)
來獲取),從而簡化了實現及開銷,避免使用額外的指標來實現樹結構。
最小(大)堆性質
- 樹根節點的值是所有堆節點值中最小(大)值。
- 樹中每個節點的子樹也都是最小(大)堆。
最小(大)堆作用
最小(大)堆能保證堆頂元素為最小,而如果使用陣列無法達到該效果。陣列如果要訪問最小值則需要遍歷查詢最小值,時間複雜度至少O(n)。而最小堆訪問最小值時間複雜度為O(1),當然天底下沒有免費的午餐,我們需要做額外的工作去維護最小(大)堆的結構,這也是需要複雜度花銷的。
當然這也是最小(大)堆的優勢,通過動態維護使得最小值的獲取代價很小,實際上維護的時間複雜度為O(logN)。而陣列則無法做到如此,如果陣列想要維護順序性則需要的複雜度至少為O(N)。這樣來看最小(大)堆的優勢就凸現出來了。
插入操作
為避免冗長累贅,我們這裡只挑最小堆作為例子進行說明,最大堆的情況與最大堆相似。
現在分別插入4 7 2 5 6 1 0 3 8
,使用一個陣列來儲存最小堆,為了幫助理解,陣列下方提供一個邏輯上的完全二叉樹的結構,兩者結合著更容易理解其中機制。首先插入4,
接著插入7,插入後檢測到樹符合最小堆要求,所以不改動。
繼續插入2,插入後檢測到不符合最小堆要求,父節點4大於右子節點2,
於是將它們對調。
繼續插入5,插入後檢測到不符合最小堆要求,父節點7大於左子節點5,
於是將它們對調。
繼續插入6,插入後檢測到樹符合最小堆要求,所以不改動。
繼續插入1,插入後檢測到不符合最小堆要求,父節點4大於左子節點1,
於是將它們對調,
對調後繼續檢測到不符合最小堆要求,父節點2大於右子節點1,
繼續將它們對調。
繼續插入0,插入後檢測到不符合最小堆要求,父節點2大於右子節點0,
於是將它們對調,
對調後繼續檢測到不符合最小堆要求,父節點1大於右子節點0,
繼續將它們對調。
繼續插入3,插入後檢測到不符合最小堆要求,父節點7大於左子節點3,
於是將它們對調,
對調後繼續檢測到不符合最小堆要求,父節點5大於左子節點3,
繼續將它們對調,然後符合最小堆要求,不必繼續往上對調。
繼續插入8,插入後檢測到樹符合最小堆要求,所以不改動。以上,完成所有元素的最小堆插入操作。
刪除操作
刪除操作其實就是刪除最小值,即最小堆樹中的根節點。主要是將樹中最後一個節點替換到被刪除的根節點,然後自頂向下遞迴調整使之符合最小堆要求。
刪除根節點0,然後將樹的最後一個節點8補到根節點上。
比較根節點的左右子節點,
因為右子節點1比較小,所以我們要進一步比較的是根節點8與右子節點1,
1小於8,於是對調。
繼續比較現在節點8的左右子節點,
因為右子節點2比較小,所以我們要進一步比較的是根節點8與右子節點2,
2小於8,於是對調。
至此,完成最小值刪除操作。
-------------推薦閱讀------------
我的開源專案彙總(機器&深度學習、NLP、網路IO、AIML、mysql協議、chatbot)
跟我交流,向我提問:
歡迎關注: