Fibonacci Heap in Python
Last Updated :
12 Apr, 2024
A Fibonacci Heap is a data structure that supports the insert
, minimum
, extract_min
, merge
, decrease_key
, and delete
operations, all amortized efficiently. It is mainly used in the implementation of Dijkstra’s shortest path algorithm and Prim’s minimum spanning tree algorithm.
Fibonacci Heap Operations and Their Amortized Time Complexities:
- Insert: O(1)
- Minimum: O(1)
- Extract Minimum: O(logn) amortized
- Merge: O(1)
- Decrease Key: O(1) amortized
- Delete: O(logn) amortized
Code:
Python3
import math
class FibonacciHeapNode:
def __init__(self, key):
self.key = key
self.degree = 0
self.parent = None
self.child = None
self.mark = False
self.left = self
self.right = self
class FibonacciHeap:
def __init__(self):
self.min_node = None
self.num_nodes = 0
def is_empty(self):
return self.min_node is None
def insert(self, key):
node = FibonacciHeapNode(key)
if self.min_node is None:
self.min_node = node
else:
node.left = self.min_node
node.right = self.min_node.right
self.min_node.right = node
node.right.left = node
if node.key < self.min_node.key:
self.min_node = node
self.num_nodes += 1
return node
def minimum(self):
if self.min_node is None:
return None
return self.min_node.key
def merge(self, other_heap):
if self.min_node is None:
self.min_node = other_heap.min_node
elif other_heap.min_node is not None:
self.min_node.right.left = other_heap.min_node.left
other_heap.min_node.left.right = self.min_node.right
self.min_node.right = other_heap.min_node
other_heap.min_node.left = self.min_node
if other_heap.min_node.key < self.min_node.key:
self.min_node = other_heap.min_node
self.num_nodes += other_heap.num_nodes
def _remove_from_root_list(self, node):
if node == node.right:
self.min_node = None
else:
node.left.right = node.right
node.right.left = node.left
if node == self.min_node:
self.min_node = node.right
def _link(self, node1, node2):
self._remove_from_root_list(node2)
node2.left = node2.right = node2
node2.parent = node1
if node1.child is None:
node1.child = node2
else:
node2.left = node1.child
node2.right = node1.child.right
node1.child.right = node2
node2.right.left = node2
node1.degree += 1
node2.mark = False
def _consolidate(self):
max_degree = math.ceil(math.log(self.num_nodes, 2))
degree_table = [None] * (max_degree + 1)
current = self.min_node
while True:
degree = current.degree
next_node = current.right
while degree_table[degree] is not None:
other = degree_table[degree]
if current.key > other.key:
current, other = other, current
self._link(current, other)
degree_table[degree] = None
degree += 1
degree_table[degree] = current
if next_node == self.min_node:
break
current = next_node
self.min_node = None
for node in degree_table:
if node is not None:
if self.min_node is None:
self.min_node = node
else:
node.left.right = node.right
node.right.left = node.left
node.left = self.min_node
node.right = self.min_node.right
self.min_node.right = node
node.right.left = node
if node.key < self.min_node.key:
self.min_node = node
def extract_min(self):
min_node = self.min_node
if min_node is not None:
if min_node.child is not None:
children = [child for child in self._iterate(min_node.child)]
for child in children:
child.parent = None
self.min_node.left.right = child
child.left = self.min_node.left
child.right = self.min_node
self.min_node.left = child
if child.key < self.min_node.key:
self.min_node = child
self._remove_from_root_list(min_node)
if min_node == min_node.right:
self.min_node = None
else:
self.min_node = min_node.right
self._consolidate()
self.num_nodes -= 1
return min_node
def _cut(self, node, parent):
if node == node.right:
parent.child = None
else:
node.left.right = node.right
node.right.left = node.left
if node == parent.child:
parent.child = node.right
parent.degree -= 1
self.min_node.left.right = node
node.left = self.min_node.left
node.right = self.min_node
self.min_node.left = node
node.parent = None
node.mark = False
def _cascade_cut(self, node):
parent = node.parent
if parent is not None:
if not node.mark:
node.mark = True
else:
self._cut(node, parent)
self._cascade_cut(parent)
def decrease_key(self, node, new_key):
if new_key > node.key:
raise ValueError("New key is greater than current key.")
node.key = new_key
parent = node.parent
if parent is not None and node.key < parent.key:
self._cut(node, parent)
self._cascade_cut(parent)
if node.key < self.min_node.key:
self.min_node = node
def delete(self, node):
self.decrease_key(node, float('-inf'))
self.extract_min()
def _iterate(self, node):
while True:
yield node
if node.child is not None:
for n in self._iterate(node.child):
yield n
node = node.right
if node == self.min_node:
break
# Example Usage
heap = FibonacciHeap()
node1 = heap.insert(3)
node2 = heap.insert(5)
node3 = heap.insert(1)
node4 = heap.insert(9)
node5 = heap.insert(7)
print("Minimum Key:", heap.minimum()) # Output: 1
heap.decrease_key(node5, 2)
print("New Minimum Key after Decrease Key:", heap.minimum()) # Output: 1
print("Extracted Minimum Key:", heap.extract_min().key) # Output: 1
print("Minimum Key after Extract Min:", heap.minimum()) # Output: 2
OutputMinimum Key: 1
New Minimum Key after Decrease Key: 1
Extracted Minimum Key: 1
Minimum Key after Extract Min: 2
Like Article
Suggest improvement
Share your thoughts in the comments
Please Login to comment...