🔼用Python來實現 Min Heap 最小堆

更新於 發佈於 閱讀時間約 13 分鐘

定義


Min-Heap 最小堆是一種特殊的樹狀資料結構,
其中每個節點的值都小於或等於其子節點的值。這意味著最小值總是位於根節點

Min-Heap 常用於實作優先權佇列 (Priority Queue)、Dijkstra 演算法、
排序以及尋找中位數等應用。


raw-image

優點

1.根節點永遠保持目前數據流的最小值

2.如果依序pop n次,將得到一個從小到大排序好的序列

3.可以override"最小"的定義,擴充為優先權佇列priority queue。


缺點

1.不支援直接取出第k小元素的操作, k > 1

2.新增、刪除都需要動態調整的成本,來保持min heap最小堆的性質

run-time成本 = O(log n)。


MinHeap的class定義 與 建構子

class MinHeap:
def __init__(self):

# 初始化時,是一個空的堆積empty heap
self.heap = []

MinHeap常見的操作


1.插入節點 insert(data)


將新元素插入到堆的最後一個位置

比較此元素和父節點的值,如果新元素比父節點還小則需要交換位置,
直到不能再向上移動為止


時間複雜度: O( log n )

  def insert(self, data):

self.heap.append(data)
self._heapify_up(len(self.heap) - 1)

2.向上調整 _heapify_up(index)


檢查heap[index]指定的元素,如果比父節點還小,就向上調整到正確的位置。

來滿足Min Heap每個節點的值都小於或等於其子節點的值的規定。


時間複雜度: O( log n )

raw-image
raw-image
raw-image
raw-image
raw-image
raw-image


  def insert(self, data):
self.heap.append(data)
self._heapify_up(len(self.heap) - 1)

def _heapify_up(self, index):
parent_index = (index - 1) // 2

if index > 0 and self.heap[index] < self.heap[parent_index]:
self.heap[index], self.heap[parent_index] = self.heap[parent_index], self.heap[index]
self._heapify_up(parent_index)
return

3.讀取最小值 get_min()


讀取整個堆積heap的最小值,也就是讀取根節點的值


時間複雜度: O( 1 )

raw-image


  def get_min(self):
if len(self.heap) == 0:
return None
return self.heap[0]

4.取出最小值 extract_min()


取出根節點(最小值),並將最後一個元素移到頂端,填補空缺,當作新的根節點。

比較新的根節點和其子節點的值,若有需要則交換位置,直到不能再向下移動為止


時間複雜度: O( log n )


5.向下調整 _heapify_down(index)


檢查heap[index]指定的元素,如果比子節點還大,就向下調整到正確的位置。

來滿足Min Heap每個節點的值都小於或等於其子節點的值的規定。


時間複雜度: O( log n )

raw-image


  def extract_min(self):
if len(self.heap) == 0:
return None
if len(self.heap) == 1:
return self.heap.pop()

root = self.heap[0]
self.heap[0] = self.heap.pop()
self._heapify_down(0)
return root


def _heapify_down(self, index):
smallest = index
left_child_index = 2 * index + 1
right_child_index = 2 * index + 2

if left_child_index < len(self.heap) and self.heap[left_child_index] < self.heap[smallest]:
smallest = left_child_index
if right_child_index < len(self.heap) and self.heap[right_child_index] < self.heap[smallest]:
smallest = right_child_index

if smallest != index:
self.heap[index], self.heap[smallest] = self.heap[smallest], self.heap[index]
self._heapify_down(smallest)

return

6.取得堆積heap的大小 size()


取得堆積的大小 = 堆積內的資料總數量。


時間複雜度: O( 1 )


raw-image


  def size(self):
return len(self.heap)

測試範例

raw-image

完整的Min Heap實作和程式碼

class MinHeap:
def __init__(self):
# 初始化時,是一個空的堆積empty heap
self.heap = []

def insert(self, data):
self.heap.append(data)
self._heapify_up(len(self.heap) - 1)

def extract_min(self):
if len(self.heap) == 0:
return None
if len(self.heap) == 1:
return self.heap.pop()

root = self.heap[0]
self.heap[0] = self.heap.pop()
self._heapify_down(0)
return root

def _heapify_up(self, index):
parent_index = (index - 1) // 2

if index > 0 and self.heap[index] < self.heap[parent_index]:
self.heap[index], self.heap[parent_index] = self.heap[parent_index], self.heap[index]
self._heapify_up(parent_index)
return

def _heapify_down(self, index):
smallest = index
left_child_index = 2 * index + 1
right_child_index = 2 * index + 2

if left_child_index < len(self.heap) and self.heap[left_child_index] < self.heap[smallest]:
smallest = left_child_index
if right_child_index < len(self.heap) and self.heap[right_child_index] < self.heap[smallest]:
smallest = right_child_index

if smallest != index:
self.heap[index], self.heap[smallest] = self.heap[smallest], self.heap[index]
self._heapify_down(smallest)

return

def get_min(self):
if len(self.heap) == 0:
return None
return self.heap[0]

def size(self):
return len(self.heap)

def test():
# Demo code
min_heap = MinHeap()
min_heap.insert(10)
min_heap.insert(15)
min_heap.insert(30)
min_heap.insert(20)
min_heap.insert(0)

print("Min element:", min_heap.get_min()) # Output: 0
print("Extracted min element:", min_heap.extract_min()) # Output: 0
print("Min element after extraction:", min_heap.get_min()) # Output: 10
print("Heap size:", min_heap.size()) # Output: 4

if __name__ == '__main__':

test()

測試輸出

Min element: 0
Extracted min element: 0
Min element after extraction: 10
Heap size: 4

結語


其實Tree就是Node的組合與推廣,每個節點最多可以有兩個子節點的樹狀資料結構。

Min Heap就是Binary Tree額外限制上層節點值 永遠小於 下層節點值的規則


讀者可以透過紙筆追蹤演算法和程式執行邏輯,測試幾個簡單的小範例,

會有更深刻的了解和體會!


Heap相關的演算法練習題與詳解


六六大順 能不能製造出順子? Hand of Straights 堆積/排序應用_Leetcode #846


實作餐廳訂位報號系統 Seat Reservation Manager Leetcode #1845


模擬: 最遠可以抵達的大樓 Furthest Building You Can Reach_Leetcode #142


滄海一粟 第k小的分數(最小堆+生成應用) Leetcode #786


最小堆應用: 雇用k名員工的最小成本 Total Cost to Hire K Workers #2462 精選75題


系統設計: 動態取出數據流的最小值 Leetcode #2336_Leetcode 精選75題解析

留言
avatar-img
留言分享你的想法!
小松鼠-avatar-img
發文者
2024/09/04
Vanessa Li 留言區的右下角有圖庫,選動畫,輸入關鍵字去搜尋。🤣
小松鼠-avatar-img
發文者
2024/09/11
動手學Python/資料結構/演算法 的目錄提及了這篇文章,趕快過去看看吧!
avatar-img
小松鼠的演算法樂園
95會員
427內容數
由有業界實戰經驗的演算法工程師, 手把手教你建立解題的框架, 一步步寫出高效、清晰易懂的解題答案。 著重在讓讀者啟發思考、理解演算法,熟悉常見的演算法模板。 深入淺出地介紹題目背後所使用的演算法意義,融會貫通演算法與資料結構的應用。 在幾個經典的題目融入一道題目的多種解法,或者同一招解不同的題目,擴展廣度,並加深印象。
2024/10/10
從Python 內建deque資料結構的角度切入, 同時了解deque 與 FIFO Queue相關的function用法。 collections.deque是一種兩端點皆可進出的雙端佇列 在兩端點高效地在O(1)常數時間內添加和刪除元素。 這使得deque非常適合實現FIFO Queue
Thumbnail
2024/10/10
從Python 內建deque資料結構的角度切入, 同時了解deque 與 FIFO Queue相關的function用法。 collections.deque是一種兩端點皆可進出的雙端佇列 在兩端點高效地在O(1)常數時間內添加和刪除元素。 這使得deque非常適合實現FIFO Queue
Thumbnail
2024/09/27
井字遊戲(OOXX)的遊戲描述 Tic Tac Toe(井字遊戲)是經典的雙人棋盤遊戲,在一個3x3的方格中進行。 每回合兩個玩家輪流選一個位置,先讓自己的符號(是 X 或 O)在 水平線、垂直線或對角線上連成一線的玩家宣告獲勝。
Thumbnail
2024/09/27
井字遊戲(OOXX)的遊戲描述 Tic Tac Toe(井字遊戲)是經典的雙人棋盤遊戲,在一個3x3的方格中進行。 每回合兩個玩家輪流選一個位置,先讓自己的符號(是 X 或 O)在 水平線、垂直線或對角線上連成一線的玩家宣告獲勝。
Thumbnail
2024/09/23
深入探討圖(Graph)的基本概念 及 最短路徑Shortest Path的尋找。 我們專注於廣度優先搜尋(BFS)演算法,以等權圖的最短路徑為例, 詳細說明如何利用BFS從起點擴散到終點,並且提供詳細的程式碼範例。 透過實作,讀者能夠更清楚理解圖論及BFS的應用,並體會水波紋擴散模型的重要性。
Thumbnail
2024/09/23
深入探討圖(Graph)的基本概念 及 最短路徑Shortest Path的尋找。 我們專注於廣度優先搜尋(BFS)演算法,以等權圖的最短路徑為例, 詳細說明如何利用BFS從起點擴散到終點,並且提供詳細的程式碼範例。 透過實作,讀者能夠更清楚理解圖論及BFS的應用,並體會水波紋擴散模型的重要性。
Thumbnail
看更多
你可能也想看
Thumbnail
「欸!這是在哪裡買的?求連結 🥺」 誰叫你太有品味,一發就讓大家跟著剁手手? 讓你回購再回購的生活好物,是時候該介紹出場了吧! 「開箱你的美好生活」現正召喚各路好物的開箱使者 🤩
Thumbnail
「欸!這是在哪裡買的?求連結 🥺」 誰叫你太有品味,一發就讓大家跟著剁手手? 讓你回購再回購的生活好物,是時候該介紹出場了吧! 「開箱你的美好生活」現正召喚各路好物的開箱使者 🤩
Thumbnail
堆積樹(Heap Tree)是一種特殊的完全二元樹,常用於查找極值。本文介紹最大堆積樹和最小堆積樹的概念、建立方法、新增節點和取出極值的方法,並預告下一篇文章將介紹雜湊表。
Thumbnail
堆積樹(Heap Tree)是一種特殊的完全二元樹,常用於查找極值。本文介紹最大堆積樹和最小堆積樹的概念、建立方法、新增節點和取出極值的方法,並預告下一篇文章將介紹雜湊表。
Thumbnail
Min-Heap 最小堆是一種特殊的樹狀資料結構, 其中每個節點的值都小於或等於其子節點的值。這意味著最小值總是位於根節點。 Min-Heap 常用於實作優先權佇列 (Priority Queue)、Dijkstra 演算法、 排序以及尋找中位數等應用。
Thumbnail
Min-Heap 最小堆是一種特殊的樹狀資料結構, 其中每個節點的值都小於或等於其子節點的值。這意味著最小值總是位於根節點。 Min-Heap 常用於實作優先權佇列 (Priority Queue)、Dijkstra 演算法、 排序以及尋找中位數等應用。
Thumbnail
在資料結構與演算法裡, 最簡單的線性資料結構除了list之外就是linked list鏈結串列了。 Linked list又有分為單向Singly linked list 和雙向Doubly linked list 在這篇文章,會從最基礎的Singly linked list開始講起。 定義
Thumbnail
在資料結構與演算法裡, 最簡單的線性資料結構除了list之外就是linked list鏈結串列了。 Linked list又有分為單向Singly linked list 和雙向Doubly linked list 在這篇文章,會從最基礎的Singly linked list開始講起。 定義
Thumbnail
題目敘述 題目會給我們一棵BST二元搜索樹的根結點root,還有一個指定的目標值key。 要求我們在樹中刪除帶有這個key值的節點,並且返回更新過後二元搜索樹的樹根root。 題目的原文敘述 測試範例 Example 1: Input: root = [5,3,6,2,4,null,
Thumbnail
題目敘述 題目會給我們一棵BST二元搜索樹的根結點root,還有一個指定的目標值key。 要求我們在樹中刪除帶有這個key值的節點,並且返回更新過後二元搜索樹的樹根root。 題目的原文敘述 測試範例 Example 1: Input: root = [5,3,6,2,4,null,
Thumbnail
題目敘述 題目的情境是設計並且實現一個包含所有正整數的數據流,以set集合的方式存在。 數據流 = {1, 2, 3, 4, ..., ∞} 要求我們去實現定義好的function介面: SmallestInfiniteSet()建構子,初始化這個包含所有正整數的數據流。 int po
Thumbnail
題目敘述 題目的情境是設計並且實現一個包含所有正整數的數據流,以set集合的方式存在。 數據流 = {1, 2, 3, 4, ..., ∞} 要求我們去實現定義好的function介面: SmallestInfiniteSet()建構子,初始化這個包含所有正整數的數據流。 int po
Thumbnail
題目到底要我們做什麼?閱讀素養有多重要,試試這題就知道!
Thumbnail
題目到底要我們做什麼?閱讀素養有多重要,試試這題就知道!
Thumbnail
題目會給們一個陣列,還有一個k值。 好陣列定義是: 有包含nums[k]的子陣列。 分數定義是: min(nums[i], nums[i+1], ..., nums[j]) * (j - i + 1) 其中, i, j 分別是子陣列的左端點和右端點 請問,好的子陣列的最大分數是多少?
Thumbnail
題目會給們一個陣列,還有一個k值。 好陣列定義是: 有包含nums[k]的子陣列。 分數定義是: min(nums[i], nums[i+1], ..., nums[j]) * (j - i + 1) 其中, i, j 分別是子陣列的左端點和右端點 請問,好的子陣列的最大分數是多少?
追蹤感興趣的內容從 Google News 追蹤更多 vocus 的最新精選內容追蹤 Google News