2024-11-18 03:03:04 +00:00
|
|
|
"""
|
|
|
|
Fibonacci Heap
|
|
|
|
A more efficient priority queue implementation that provides amortized time bounds
|
|
|
|
that are better than those of the binary and binomial heaps.
|
|
|
|
|
|
|
|
Operations supported:
|
|
|
|
- Insert: O(1) amortized
|
|
|
|
- Find minimum: O(1)
|
|
|
|
- Delete minimum: O(log n) amortized
|
|
|
|
- Decrease key: O(1) amortized
|
|
|
|
- Merge: O(1)
|
|
|
|
"""
|
2024-11-17 03:30:25 +00:00
|
|
|
|
|
|
|
|
2024-11-13 08:36:58 +00:00
|
|
|
class Node:
|
2024-11-18 03:03:04 +00:00
|
|
|
"""
|
|
|
|
Node in a Fibonacci heap containing:
|
|
|
|
- value
|
|
|
|
- parent, child, and sibling links
|
|
|
|
- degree (number of children)
|
|
|
|
- mark (whether the node has lost a child since
|
|
|
|
becoming a child of its current parent)
|
2024-11-13 08:36:58 +00:00
|
|
|
"""
|
|
|
|
|
2024-11-18 03:03:04 +00:00
|
|
|
def __init__(self, val):
|
|
|
|
self.val = val
|
|
|
|
self.parent = None
|
|
|
|
self.child = None
|
2024-11-13 08:36:58 +00:00
|
|
|
self.left = self
|
|
|
|
self.right = self
|
2024-11-18 03:03:04 +00:00
|
|
|
self.degree = 0
|
|
|
|
self.mark = False
|
|
|
|
|
|
|
|
def add_sibling(self, node):
|
|
|
|
"""Add node as a sibling"""
|
|
|
|
node.left = self
|
|
|
|
node.right = self.right
|
|
|
|
self.right.left = node
|
|
|
|
self.right = node
|
|
|
|
|
|
|
|
def add_child(self, node):
|
|
|
|
"""Add node as a child"""
|
|
|
|
node.parent = self
|
|
|
|
if not self.child:
|
|
|
|
self.child = node
|
|
|
|
else:
|
|
|
|
self.child.add_sibling(node)
|
|
|
|
self.degree += 1
|
|
|
|
|
|
|
|
def remove(self):
|
|
|
|
"""Remove this node from its sibling list"""
|
|
|
|
self.left.right = self.right
|
|
|
|
self.right.left = self.left
|
2024-11-13 08:36:58 +00:00
|
|
|
|
|
|
|
|
|
|
|
class FibonacciHeap:
|
2024-11-18 03:03:04 +00:00
|
|
|
"""
|
|
|
|
Min-oriented Fibonacci heap implementation.
|
|
|
|
|
|
|
|
Example:
|
|
|
|
>>> heap = FibonacciHeap()
|
|
|
|
>>> heap.insert(3)
|
|
|
|
>>> heap.insert(2)
|
|
|
|
>>> heap.insert(15)
|
|
|
|
>>> heap.peek()
|
|
|
|
2
|
|
|
|
>>> heap.delete_min()
|
|
|
|
2
|
|
|
|
>>> heap.peek()
|
|
|
|
3
|
2024-11-13 08:36:58 +00:00
|
|
|
"""
|
|
|
|
|
2024-11-18 03:03:04 +00:00
|
|
|
def __init__(self):
|
|
|
|
self.min_node = None
|
|
|
|
self.size = 0
|
2024-11-13 08:36:58 +00:00
|
|
|
|
2024-11-18 03:03:04 +00:00
|
|
|
def is_empty(self):
|
|
|
|
"""Return True if heap is empty"""
|
|
|
|
return self.min_node is None
|
2024-11-13 08:36:58 +00:00
|
|
|
|
2024-11-18 03:03:04 +00:00
|
|
|
def insert(self, val):
|
|
|
|
"""Insert a new key into the heap"""
|
|
|
|
node = Node(val)
|
|
|
|
if not self.min_node:
|
|
|
|
self.min_node = node
|
2024-11-13 08:36:58 +00:00
|
|
|
else:
|
2024-11-18 03:03:04 +00:00
|
|
|
self.min_node.add_sibling(node)
|
|
|
|
if node.val < self.min_node.val:
|
|
|
|
self.min_node = node
|
|
|
|
self.size += 1
|
|
|
|
return node
|
|
|
|
|
|
|
|
def peek(self):
|
|
|
|
"""Return minimum value without removing it"""
|
|
|
|
if not self.min_node:
|
|
|
|
raise IndexError("Heap is empty")
|
|
|
|
return self.min_node.val
|
|
|
|
|
|
|
|
def merge_heaps(self, other):
|
|
|
|
"""Merge another Fibonacci heap with this one"""
|
|
|
|
if not other.min_node:
|
|
|
|
return
|
|
|
|
if not self.min_node:
|
|
|
|
self.min_node = other.min_node
|
|
|
|
else:
|
|
|
|
# Merge root lists
|
|
|
|
self.min_node.right.left = other.min_node.left
|
|
|
|
other.min_node.left.right = self.min_node.right
|
|
|
|
self.min_node.right = other.min_node
|
|
|
|
other.min_node.left = self.min_node
|
|
|
|
|
|
|
|
if other.min_node.val < self.min_node.val:
|
|
|
|
self.min_node = other.min_node
|
|
|
|
|
|
|
|
self.size += other.size
|
|
|
|
|
|
|
|
def __link_trees(self, node1, node2):
|
|
|
|
"""Link two trees of same degree"""
|
|
|
|
node1.remove()
|
|
|
|
if node2.child:
|
|
|
|
node2.child.add_sibling(node1)
|
|
|
|
else:
|
|
|
|
node2.child = node1
|
|
|
|
node1.parent = node2
|
|
|
|
node2.degree += 1
|
|
|
|
node1.mark = False
|
2024-11-13 08:36:58 +00:00
|
|
|
|
2024-11-18 03:03:04 +00:00
|
|
|
def delete_min(self):
|
|
|
|
"""Remove and return the minimum value"""
|
|
|
|
if not self.min_node:
|
|
|
|
raise IndexError("Heap is empty")
|
2024-11-13 08:36:58 +00:00
|
|
|
|
2024-11-18 03:03:04 +00:00
|
|
|
min_val = self.min_node.val
|
2024-11-13 08:36:58 +00:00
|
|
|
|
2024-11-18 03:03:04 +00:00
|
|
|
# Add all children to root list
|
|
|
|
if self.min_node.child:
|
|
|
|
curr = self.min_node.child
|
2024-11-13 08:36:58 +00:00
|
|
|
while True:
|
2024-11-18 03:03:04 +00:00
|
|
|
next_node = curr.right
|
|
|
|
curr.parent = None
|
|
|
|
curr.mark = False
|
|
|
|
self.min_node.add_sibling(curr)
|
|
|
|
if curr.right == self.min_node.child:
|
2024-11-13 08:36:58 +00:00
|
|
|
break
|
2024-11-18 03:03:04 +00:00
|
|
|
curr = next_node
|
2024-11-13 08:36:58 +00:00
|
|
|
|
2024-11-18 03:03:04 +00:00
|
|
|
# Remove minimum node
|
|
|
|
if self.min_node.right == self.min_node:
|
|
|
|
self.min_node = None
|
|
|
|
else:
|
|
|
|
self.min_node.remove()
|
|
|
|
self.min_node = self.min_node.right
|
|
|
|
self.__consolidate()
|
2024-11-13 08:36:58 +00:00
|
|
|
|
2024-11-18 03:03:04 +00:00
|
|
|
self.size -= 1
|
|
|
|
return min_val
|
2024-11-13 08:36:58 +00:00
|
|
|
|
2024-11-18 03:03:04 +00:00
|
|
|
def __consolidate(self):
|
|
|
|
"""Consolidate trees after delete_min"""
|
|
|
|
max_degree = int(self.size**0.5) + 1
|
|
|
|
degree_table = [None] * max_degree
|
2024-11-13 08:36:58 +00:00
|
|
|
|
2024-11-18 03:03:04 +00:00
|
|
|
# Collect all roots
|
|
|
|
roots = []
|
|
|
|
curr = self.min_node
|
|
|
|
while True:
|
|
|
|
roots.append(curr)
|
|
|
|
curr = curr.right
|
|
|
|
if curr == self.min_node:
|
|
|
|
break
|
|
|
|
|
|
|
|
# Consolidate trees
|
|
|
|
for root in roots:
|
|
|
|
degree = root.degree
|
|
|
|
while degree_table[degree]:
|
|
|
|
other = degree_table[degree]
|
|
|
|
if root.val > other.val:
|
|
|
|
root, other = other, root
|
|
|
|
self.__link_trees(other, root)
|
|
|
|
degree_table[degree] = None
|
|
|
|
degree += 1
|
|
|
|
degree_table[degree] = root
|
|
|
|
|
|
|
|
# Find new minimum
|
|
|
|
self.min_node = None
|
|
|
|
for degree in range(max_degree):
|
|
|
|
if degree_table[degree]:
|
|
|
|
if not self.min_node:
|
|
|
|
self.min_node = degree_table[degree]
|
|
|
|
self.min_node.left = self.min_node
|
|
|
|
self.min_node.right = self.min_node
|
2024-11-13 08:36:58 +00:00
|
|
|
else:
|
2024-11-18 03:03:04 +00:00
|
|
|
self.min_node.add_sibling(degree_table[degree])
|
|
|
|
if degree_table[degree].val < self.min_node.val:
|
|
|
|
self.min_node = degree_table[degree]
|
2024-11-13 08:36:58 +00:00
|
|
|
|
2024-11-18 03:03:04 +00:00
|
|
|
def decrease_key(self, node, new_val):
|
|
|
|
"""Decrease the value of a node"""
|
|
|
|
if new_val > node.val:
|
|
|
|
raise ValueError("New value is greater than current value")
|
2024-11-13 08:36:58 +00:00
|
|
|
|
2024-11-18 03:03:04 +00:00
|
|
|
node.val = new_val
|
|
|
|
parent = node.parent
|
2024-11-13 08:36:58 +00:00
|
|
|
|
2024-11-18 03:03:04 +00:00
|
|
|
if parent and node.val < parent.val:
|
|
|
|
self.__cut(node, parent)
|
|
|
|
self.__cascading_cut(parent)
|
2024-11-13 08:36:58 +00:00
|
|
|
|
2024-11-18 03:03:04 +00:00
|
|
|
if node.val < self.min_node.val:
|
2024-11-13 08:36:58 +00:00
|
|
|
self.min_node = node
|
|
|
|
|
2024-11-18 03:03:04 +00:00
|
|
|
def __cut(self, node, parent):
|
|
|
|
"""Cut a node from its parent"""
|
|
|
|
parent.degree -= 1
|
|
|
|
if parent.child == node:
|
|
|
|
parent.child = node.right if node.right != node else None
|
|
|
|
node.remove()
|
|
|
|
node.left = node
|
|
|
|
node.right = node
|
|
|
|
node.parent = None
|
|
|
|
node.mark = False
|
|
|
|
self.min_node.add_sibling(node)
|
|
|
|
|
|
|
|
def __cascading_cut(self, node):
|
|
|
|
"""Perform cascading cut operation"""
|
|
|
|
if parent := node.parent:
|
|
|
|
if not node.mark:
|
|
|
|
node.mark = True
|
2024-11-13 08:36:58 +00:00
|
|
|
else:
|
2024-11-18 03:03:04 +00:00
|
|
|
self.__cut(node, parent)
|
|
|
|
self.__cascading_cut(parent)
|
|
|
|
|
|
|
|
def __str__(self):
|
|
|
|
"""String representation of the heap"""
|
|
|
|
if not self.min_node:
|
|
|
|
return "Empty heap"
|
2024-11-13 08:36:58 +00:00
|
|
|
|
2024-11-18 03:03:04 +00:00
|
|
|
def print_tree(node, level=0):
|
|
|
|
result = []
|
|
|
|
curr = node
|
|
|
|
while True:
|
|
|
|
result.append("-" * level + str(curr.val))
|
|
|
|
if curr.child:
|
|
|
|
result.extend(print_tree(curr.child, level + 1))
|
|
|
|
curr = curr.right
|
|
|
|
if curr == node:
|
|
|
|
break
|
|
|
|
return result
|
2024-11-13 08:36:58 +00:00
|
|
|
|
2024-11-18 03:03:04 +00:00
|
|
|
return "\n".join(print_tree(self.min_node))
|
2024-11-13 08:36:58 +00:00
|
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
import doctest
|
|
|
|
|
|
|
|
doctest.testmod()
|