Fibonacci Heap
In the world of computer science, data structures are essential for organizing and managing data efficiently. Among the many data structures, the Fibonacci Heap stands out due to its impressive amortized time complexity for various operations. It is particularly useful when working with priority queues, which are a vital component in algorithms such as Dijkstra’s shortest path and Prim's minimum spanning tree.
A Fibonacci Heap is a type of heap data structure that consists of a collection of min-heaps organized in a more flexible way. It was introduced by Michael L. Fredman and Robert E. Tarjan in 1984 and is a highly efficient data structure for priority queues. The Fibonacci Heap is named after the Fibonacci numbers because of its structure, which resembles a Fibonacci tree.
Fibonacci Heaps offer better time complexities for several operations than traditional binary heaps, making them particularly advantageous in scenarios where multiple heap operations need to be performed in sequence, such as in network routing algorithms or graph traversal.
Fibonacci Heaps have several key properties that distinguish them from other heap data structures:
Fibonacci Heaps support a variety of operations, each with different time complexities. Some of the key operations include:
x
into the heap.x
to a new value k
.x
from the heap.The Fibonacci Heap provides the following amortized time complexities for its operations:
The Fibonacci Heap outperforms other heaps, such as binary heaps, for algorithms like Dijkstra’s shortest path and Prim's minimum spanning tree, where many Decrease-Key and Extract-Min operations are required.
Let’s now take a look at a basic implementation of the Fibonacci Heap in Python.
class FibonacciHeapNode:
def __init__(self, key):
self.key = key
self.degree = 0
self.parent = None
self.child = None
self.mark = False
self.next = self
self.prev = self
class FibonacciHeap:
def __init__(self):
self.min = None
self.total_nodes = 0
def insert(self, key):
new_node = FibonacciHeapNode(key)
if self.min is None:
self.min = new_node
else:
# Linking new node into the root list
self._link(self.min, new_node)
if key < self.min.key:
self.min = new_node
self.total_nodes += 1
def _link(self, node1, node2):
# Connect node2 to node1's next
node1_next = node1.next
node1.next = node2
node2.prev = node1
node2.next = node1_next
node1_next.prev = node2
def extract_min(self):
if self.min is None:
return None
min_node = self.min
if min_node.child is not None:
child = min_node.child
while True:
next_child = child.next
child.prev = self.min.prev
self.min.prev.next = child
child.next = self.min
self.min.prev = child
child = next_child
if child == min_node.child:
break
self.min = min_node.next
self.total_nodes -= 1
return min_node.key
def minimum(self):
if self.min is None:
return None
return self.min.key
Fibonacci Heaps are especially useful in the following scenarios: