🔼用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題解析

86會員
425內容數
由有業界實戰經驗的演算法工程師, 手把手教你建立解題的框架, 一步步寫出高效、清晰易懂的解題答案。 著重在讓讀者啟發思考、理解演算法,熟悉常見的演算法模板。 深入淺出地介紹題目背後所使用的演算法意義,融會貫通演算法與資料結構的應用。 在幾個經典的題目融入一道題目的多種解法,或者同一招解不同的題目,擴展廣度,並加深印象。
留言0
查看全部
發表第一個留言支持創作者!
Prefix Sum(前綴和)是一種用於計算陣列中任意區間和的高效方法。 前綴和算是一種犧牲空間換取時間效能提升的策略。 這在需要頻繁查詢區間和的情況下特別有用。 一開始,初始化時花費O(n)時間,掃描每個元素累加,建立一個prefix sum table, 接著,提供query介面查詢區間和
二元搜尋樹(Binary Search Tree,簡稱 BST)是一種特殊的二元樹結構, 具有以下特性: 左子樹:左子樹上所有節點的值均小於該節點的值。 右子樹:右子樹上所有節點的值均大於該節點的值。 無重複值:每個節點的值都是唯一的。 這些特性使得二元搜尋樹在搜尋、插入和刪除操作具有較佳的效能。
接著來進入圖論的重點之一,Tree與Binary Tree。 二元樹(Binary Tree)是一種樹狀數據結構,其中每個節點最多有兩個子節點,通常稱為左子節點和右子節點。這些子節點可以是其他節點或空節點(即無子節點)。 二元樹是其他進階樹的基礎,可延伸推廣到Binary Search Tree
今天,我們將用Python list來實現Disjoint Set (併查集,另外也有人稱之為Union-Find)。 Disjoint Set適合用於處理一些子集合的合併和根節點的查找操作。 這種資料結構在圖論中非常有用,特別是在解決連通性相關問題的應用。
在之前的教學中,已經學會了用雙向鏈結串列來實作Stack 堆疊。 今天,要用另一種底層資列結構,python list,來實作Stack 堆疊。 讀者可以從中發現,因為python list的功能和function實作已經很豐富, 所以使用起來,相當直覺,也簡單許多。
在這次的BMI(身體質量指標)計算的續集裡,將學會funciton的基本觀念與實作, 把常用的功能包裝成可重複利用的元件: function。
Prefix Sum(前綴和)是一種用於計算陣列中任意區間和的高效方法。 前綴和算是一種犧牲空間換取時間效能提升的策略。 這在需要頻繁查詢區間和的情況下特別有用。 一開始,初始化時花費O(n)時間,掃描每個元素累加,建立一個prefix sum table, 接著,提供query介面查詢區間和
二元搜尋樹(Binary Search Tree,簡稱 BST)是一種特殊的二元樹結構, 具有以下特性: 左子樹:左子樹上所有節點的值均小於該節點的值。 右子樹:右子樹上所有節點的值均大於該節點的值。 無重複值:每個節點的值都是唯一的。 這些特性使得二元搜尋樹在搜尋、插入和刪除操作具有較佳的效能。
接著來進入圖論的重點之一,Tree與Binary Tree。 二元樹(Binary Tree)是一種樹狀數據結構,其中每個節點最多有兩個子節點,通常稱為左子節點和右子節點。這些子節點可以是其他節點或空節點(即無子節點)。 二元樹是其他進階樹的基礎,可延伸推廣到Binary Search Tree
今天,我們將用Python list來實現Disjoint Set (併查集,另外也有人稱之為Union-Find)。 Disjoint Set適合用於處理一些子集合的合併和根節點的查找操作。 這種資料結構在圖論中非常有用,特別是在解決連通性相關問題的應用。
在之前的教學中,已經學會了用雙向鏈結串列來實作Stack 堆疊。 今天,要用另一種底層資列結構,python list,來實作Stack 堆疊。 讀者可以從中發現,因為python list的功能和function實作已經很豐富, 所以使用起來,相當直覺,也簡單許多。
在這次的BMI(身體質量指標)計算的續集裡,將學會funciton的基本觀念與實作, 把常用的功能包裝成可重複利用的元件: function。
你可能也想看
Google News 追蹤
Thumbnail
這個秋,Chill 嗨嗨!穿搭美美去賞楓,裝備款款去露營⋯⋯你的秋天怎麼過?秋日 To Do List 等你分享! 秋季全站徵文,我們準備了五個創作主題,參賽還有機會獲得「火烤兩用鍋」,一起來看看如何參加吧~
Thumbnail
美國總統大選只剩下三天, 我們觀察一整週民調與金融市場的變化(包含賭局), 到本週五下午3:00前為止, 誰是美國總統幾乎大概可以猜到60-70%的機率, 本篇文章就是以大選結局為主軸來討論近期甚至到未來四年美股可能的改變
Thumbnail
Faker昨天真的太扯了,中國主播王多多點評的話更是精妙,分享給各位 王多多的點評 「Faker是我們的處境,他是LPL永遠繞不開的一個人和話題,所以我們特別渴望在決賽跟他相遇,去直面我們的處境。 我們曾經稱他為最高的山,最長的河,以為山海就是盡頭,可是Faker用他28歲的年齡...
Thumbnail
題目敘述 Find K-th Smallest Pair Distance 給定一個輸入陣列nums和 參數k。 請找出第k小的pair distance是多少? pair distance定義為 abs( nums[i] - nums[j]), i 不等於j 也就是任意兩陣列元素差值的絕對值
Thumbnail
題目敘述 Sort Array by Increasing Frequency Leetcode #1636 給定一個輸入陣列,請依照出現頻率的多寡從低頻到高頻排列陣列元素。 如果有兩個元素的出現頻率相同,依照元素大小從大到小排列。 測試範例 Example 1: Input: nums
Thumbnail
給定一個輸入陣列,每一個tuple代表節點之間了從屬關係。 請從從屬關係重建整顆二元樹,並且返回整顆二元樹的根結點。
Thumbnail
題目敘述 Binary Tree Maximum Path Sum 給定一個二元樹,請找出最大的區間路徑和是多少? 註: 區間路徑和 = 某個節點a -> 某個節點b的路徑節點值總和。
Thumbnail
Minimum Path Sum 給定一個矩陣,每個格子點代表經過的對應成本。 每回合可以往右移動一格或往下移動一格。 請問從起點左上角 走到 終點右下角的最小路徑成本總和是多少?
Thumbnail
給定一個整數陣列hand代表手牌點數,和參數groupSize。請問能不能每groupSize牌一組,每一組都拼出順子? 如果可以,返回True。如果無解,返回False。演算法使用最小堆積或排序。關鍵知識點:從小到大掃描每張牌,檢查能不能組成牌組長度為groupSize的順子即可。
Thumbnail
給定一個字串陣列,請把它們所共有的字元伴隨著出現次數輸出。這篇文章介紹如何使用字典統計出現次數,和字典取交集的方法來解決此問題。並提供了複雜度分析和關鍵知識點。
Thumbnail
題目敘述 題目會給我們一棵二元搜索樹的根結點root,還有一個指定的目標值val。 要求我們找出在樹中對應到目標值val的節點,假如找不到,請回傳null( null在Python就是None)。 題目的原文敘述 測試範例 Example 1: Input: root = [4,2,
Thumbnail
題目敘述 題目會給我們一顆二元樹的根結點,請我們列出每一層最右邊的節點值,以陣列的形式返回答案。 題目的原文敘述 測試範例 Example 1: Input: root = [1,2,3,null,5,null,4] Output: [1,3,4] 每一層最右邊的節點值分別是1, 3,
Thumbnail
題目敘述 題目的情境是設計並且實現一個包含所有正整數的數據流,以set集合的方式存在。 數據流 = {1, 2, 3, 4, ..., ∞} 要求我們去實現定義好的function介面: SmallestInfiniteSet()建構子,初始化這個包含所有正整數的數據流。 int po
Thumbnail
這個秋,Chill 嗨嗨!穿搭美美去賞楓,裝備款款去露營⋯⋯你的秋天怎麼過?秋日 To Do List 等你分享! 秋季全站徵文,我們準備了五個創作主題,參賽還有機會獲得「火烤兩用鍋」,一起來看看如何參加吧~
Thumbnail
美國總統大選只剩下三天, 我們觀察一整週民調與金融市場的變化(包含賭局), 到本週五下午3:00前為止, 誰是美國總統幾乎大概可以猜到60-70%的機率, 本篇文章就是以大選結局為主軸來討論近期甚至到未來四年美股可能的改變
Thumbnail
Faker昨天真的太扯了,中國主播王多多點評的話更是精妙,分享給各位 王多多的點評 「Faker是我們的處境,他是LPL永遠繞不開的一個人和話題,所以我們特別渴望在決賽跟他相遇,去直面我們的處境。 我們曾經稱他為最高的山,最長的河,以為山海就是盡頭,可是Faker用他28歲的年齡...
Thumbnail
題目敘述 Find K-th Smallest Pair Distance 給定一個輸入陣列nums和 參數k。 請找出第k小的pair distance是多少? pair distance定義為 abs( nums[i] - nums[j]), i 不等於j 也就是任意兩陣列元素差值的絕對值
Thumbnail
題目敘述 Sort Array by Increasing Frequency Leetcode #1636 給定一個輸入陣列,請依照出現頻率的多寡從低頻到高頻排列陣列元素。 如果有兩個元素的出現頻率相同,依照元素大小從大到小排列。 測試範例 Example 1: Input: nums
Thumbnail
給定一個輸入陣列,每一個tuple代表節點之間了從屬關係。 請從從屬關係重建整顆二元樹,並且返回整顆二元樹的根結點。
Thumbnail
題目敘述 Binary Tree Maximum Path Sum 給定一個二元樹,請找出最大的區間路徑和是多少? 註: 區間路徑和 = 某個節點a -> 某個節點b的路徑節點值總和。
Thumbnail
Minimum Path Sum 給定一個矩陣,每個格子點代表經過的對應成本。 每回合可以往右移動一格或往下移動一格。 請問從起點左上角 走到 終點右下角的最小路徑成本總和是多少?
Thumbnail
給定一個整數陣列hand代表手牌點數,和參數groupSize。請問能不能每groupSize牌一組,每一組都拼出順子? 如果可以,返回True。如果無解,返回False。演算法使用最小堆積或排序。關鍵知識點:從小到大掃描每張牌,檢查能不能組成牌組長度為groupSize的順子即可。
Thumbnail
給定一個字串陣列,請把它們所共有的字元伴隨著出現次數輸出。這篇文章介紹如何使用字典統計出現次數,和字典取交集的方法來解決此問題。並提供了複雜度分析和關鍵知識點。
Thumbnail
題目敘述 題目會給我們一棵二元搜索樹的根結點root,還有一個指定的目標值val。 要求我們找出在樹中對應到目標值val的節點,假如找不到,請回傳null( null在Python就是None)。 題目的原文敘述 測試範例 Example 1: Input: root = [4,2,
Thumbnail
題目敘述 題目會給我們一顆二元樹的根結點,請我們列出每一層最右邊的節點值,以陣列的形式返回答案。 題目的原文敘述 測試範例 Example 1: Input: root = [1,2,3,null,5,null,4] Output: [1,3,4] 每一層最右邊的節點值分別是1, 3,
Thumbnail
題目敘述 題目的情境是設計並且實現一個包含所有正整數的數據流,以set集合的方式存在。 數據流 = {1, 2, 3, 4, ..., ∞} 要求我們去實現定義好的function介面: SmallestInfiniteSet()建構子,初始化這個包含所有正整數的數據流。 int po