一、二項堆(Binomial Heap):理解「合并操作」的優化
二項堆的核心優勢是高效合并,類似 “二進制加法”。我們通過「合并兩個二項堆」的偽代碼和步驟來理解:
核心結構偽代碼:
class BinomialTreeNode:def __init__(self, key):self.key = key # 節點值self.degree = 0 # 度數(子節點數量)self.parent = None # 父節點self.children = [] # 子節點列表(按度數從小到大排列)class BinomialHeap:def __init__(self):self.trees = [] # 存儲二項樹(按度數從小到大排序,每個度數最多1棵)self.min_node = None # 指向最小元素節點
關鍵操作:合并兩個二項堆(類似二進制加法)
def merge_heaps(h1, h2):# 1. 合并兩個堆的樹列表(按度數從小到大)merged = []i = j = 0while i < len(h1.trees) and j < len(h2.trees):t1 = h1.trees[i]t2 = h2.trees[j]if t1.degree < t2.degree:merged.append(t1)i += 1else:merged.append(t2)j += 1merged.extend(h1.trees[i:])merged.extend(h2.trees[j:])# 2. 合并相同度數的樹(類似二進制進位)result = BinomialHeap()carry = None # 用于暫存合并后的樹for tree in merged:if carry is None:carry = treeelse:if carry.degree == tree.degree:# 合并兩棵同度數的樹(小根作為父節點)if carry.key > tree.key:carry, tree = tree, carry # 保證carry是小根tree.parent = carrycarry.children.append(tree)carry.degree += 1 # 度數+1else:# 度數不同,直接加入結果result.trees.append(carry)carry = treeif carry is not None:result.trees.append(carry)# 3. 更新最小節點result.update_min()return result
步驟拆解(合并示例):
假設?h1
?有一棵樹?B2
(度數 2),h2
?有一棵樹?B2
(度數 2):
- 合并樹列表后,得到兩個度數為 2 的樹。
- 合并這兩棵樹:較小根節點作為父,另一棵作為子,得到一棵?
B3
(度數 3)。 - 最終結果堆只包含?
B3
,完成合并。
核心理解:二項堆的合并像 “二進制加法”,同度數樹合并后度數 + 1,效率遠高于二叉堆的 O (n) 合并。
二、斐波那契堆(Fibonacci Heap):理解「延遲操作」
斐波那契堆的高效源于 “不立即處理沖突,延遲到必要時”。以「插入」和「提取最小」為例:
核心結構偽代碼:
class FibonacciNode:def __init__(self, key):self.key = keyself.parent = Noneself.children = [] # 子節點列表self.degree = 0 # 子節點數量self.marked = False # 標記:是否失去過子節點class FibonacciHeap:def __init__(self):self.root_list = [] # 根節點列表(所有樹的根)self.min_node = None # 最小節點指針self.node_count = 0 # 總節點數
1. 插入操作(O (1),體現延遲思想):
def insert(self, key):new_node = FibonacciNode(key)self.root_list.append(new_node) # 直接加入根列表,不合并# 更新最小節點if self.min_node is None or new_node.key < self.min_node.key:self.min_node = new_nodeself.node_count += 1
步驟理解:插入時直接把新節點作為一棵新樹加入根列表,不處理任何合并,所以是 O (1)。
2. 提取最小節點(觸發延遲處理,攤還 O (log n)):
def extract_min(self):if self.min_node is None:return None# 1. 把最小節點的子節點加入根列表(失去父節點)for child in self.min_node.children:self.root_list.append(child)child.parent = None# 2. 從根列表移除最小節點self.root_list.remove(self.min_node)self.node_count -= 1# 3. 延遲處理:合并相同度數的樹(此時才清理結構)self.consolidate() # 核心:合并根列表中同度數的樹# 4. 重新找最小節點self.min_node = self.find_min_in_root_list()return self.min_node.key
步驟理解:只有提取最小節點時,才會合并根列表中同度數的樹(類似二項堆的合并),這個操作的代價被 “攤還” 到之前的插入操作上,所以整體攤還復雜度是 O (log n)。
核心理解:斐波那契堆通過 “延遲合并” 把復雜操作推遲,讓簡單操作(插入、合并)變得極快。
三、配對堆(Pairing Heap):理解「簡單高效的合并」
配對堆的核心是極簡的結構和兩兩合并策略,實現簡單卻高效。
核心結構偽代碼:
class PairingNode:def __init__(self, key):self.key = keyself.children = [] # 子節點列表(子堆)class PairingHeap:def __init__(self, key=None):self.root = PairingNode(key) if key is not None else None
1. 合并操作(O (1),體現簡單性):
def merge(h1, h2):# 比較兩個根,小根作為父節點,大根作為子節點if h1 is None:return h2if h2 is None:return h1if h1.root.key > h2.root.key:h1, h2 = h2, h1 # 保證h1根更小# 把h2作為h1的子節點h1.root.children.append(h2.root)return h1
步驟理解:合并兩個配對堆時,只需把根更大的堆作為子節點掛到根更小的堆上,一步完成,所以是 O (1)。
2. 提取最小節點(用 “配對法” 合并子堆):
def extract_min(self):if self.root is None:return Nonemin_key = self.root.key# 合并所有子節點(核心:配對法)if self.root.children:self.root = self.pairwise_merge(self.root.children)else:self.root = Nonereturn min_keydef pairwise_merge(children):# 第一步:兩兩合并子節點if len(children) == 0:return Noneif len(children) == 1:return children[0]# 兩兩合并,遞歸處理剩余merged = []for i in range(0, len(children)-1, 2):merged_child = merge(children[i], children[i+1])merged.append(merged_child)# 若有奇數個,最后一個單獨處理if len(children) % 2 == 1:merged.append(children[-1])# 第二步:依次合并結果return pairwise_merge(merged)
步驟理解:提取最小節點(根節點)后,將其子節點兩兩合并,再遞歸合并結果,確保合并效率。這種 “配對合并” 策略讓攤還復雜度接近 O (log n)。
核心理解:配對堆用最簡單的結構(單根 + 子堆列表)和合并策略,在實踐中比理論更高效,適合工程實現。
總結:通過核心操作理解高級堆
堆類型 | 最能體現特性的操作 | 操作邏輯核心 | 為什么高效? |
---|---|---|---|
二項堆 | 合并 | 按度數從小到大合并,同度數樹合并為更高一度樹(類似二進制加法) | 合并復雜度從 O (n) 降為 O (log n) |
斐波那契堆 | 插入 | 直接加入根列表,不合并(延遲處理) | 簡單操作 O (1),復雜操作攤還處理 |
配對堆 | 合并 | 小根作為父,大根作為子,提取時兩兩合并子堆 | 結構極簡,合并邏輯簡單,常數小 |
對于初學者,不需要死記代碼,重點理解:
- 二項堆是 “有序合并的基礎”;
- 斐波那契堆是 “延遲優化的極致”;
- 配對堆是 “簡單實用的優選”。
它們的設計思想(如延遲操作、結構化合并)對理解更復雜的算法非常有幫助。