Added tests and docstrings to fibonacci_heap.py

This commit is contained in:
mcawezome 2024-11-18 12:10:01 +08:00
parent ea5a187b0a
commit a92936bd01

View File

@ -2,6 +2,7 @@
Fibonacci Heap Fibonacci Heap
A more efficient priority queue implementation that provides amortized time bounds A more efficient priority queue implementation that provides amortized time bounds
that are better than those of the binary and binomial heaps. that are better than those of the binary and binomial heaps.
reference: https://en.wikipedia.org/wiki/Fibonacci_heap
Operations supported: Operations supported:
- Insert: O(1) amortized - Insert: O(1) amortized
@ -11,17 +12,22 @@ Operations supported:
- Merge: O(1) - Merge: O(1)
""" """
class Node: class Node:
""" """
Node in a Fibonacci heap containing: A node in a Fibonacci heap.
- 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)
"""
Args:
val: The value stored in the node.
Attributes:
val: The value stored in the node.
parent: Reference to parent node.
child: Reference to one child node.
left: Reference to left sibling.
right: Reference to right sibling.
degree: Number of children.
mark: Boolean indicating if node has lost a child.
"""
def __init__(self, val): def __init__(self, val):
self.val = val self.val = val
self.parent = None self.parent = None
@ -32,14 +38,24 @@ class Node:
self.mark = False self.mark = False
def add_sibling(self, node): def add_sibling(self, node):
"""Add node as a sibling""" """
Adds a node as a sibling to the current node.
Args:
node: The node to add as a sibling.
"""
node.left = self node.left = self
node.right = self.right node.right = self.right
self.right.left = node self.right.left = node
self.right = node self.right = node
def add_child(self, node): def add_child(self, node):
"""Add node as a child""" """
Adds a node as a child of the current node.
Args:
node: The node to add as a child.
"""
node.parent = self node.parent = self
if not self.child: if not self.child:
self.child = node self.child = node
@ -48,38 +64,65 @@ class Node:
self.degree += 1 self.degree += 1
def remove(self): def remove(self):
"""Remove this node from its sibling list""" """Removes this node from its sibling list."""
self.left.right = self.right self.left.right = self.right
self.right.left = self.left self.right.left = self.left
class FibonacciHeap: class FibonacciHeap:
""" """
Min-oriented Fibonacci heap implementation. A Fibonacci heap implementation providing
amortized efficient priority queue operations.
This implementation provides the following time complexities:
- Insert: O(1) amortized
- Find minimum: O(1)
- Delete minimum: O(log n) amortized
- Decrease key: O(1) amortized
- Merge: O(1)
Example: Example:
>>> heap = FibonacciHeap() >>> heap = FibonacciHeap()
>>> heap.insert(3) >>> node1 = heap.insert(3)
>>> heap.insert(2) >>> node2 = heap.insert(2)
>>> heap.insert(15) >>> node3 = heap.insert(15)
>>> heap.peek() >>> heap.peek()
2 2
>>> heap.delete_min() >>> heap.delete_min()
2 2
>>> heap.peek() >>> heap.peek()
3 3
>>> other_heap = FibonacciHeap()
>>> node4 = other_heap.insert(1)
>>> heap.merge_heaps(other_heap)
>>> heap.peek()
1
""" """
def __init__(self): def __init__(self):
"""Initializes an empty Fibonacci heap."""
self.min_node = None self.min_node = None
self.size = 0 self.size = 0
def is_empty(self): def is_empty(self):
"""Return True if heap is empty""" """
Checks if the heap is empty.
Returns:
bool: True if heap is empty, False otherwise.
"""
return self.min_node is None return self.min_node is None
def insert(self, val): def insert(self, val):
"""Insert a new key into the heap""" """
Inserts a new value into the heap.
Args:
val: Value to insert.
Returns:
Node: The newly created node.
"""
node = Node(val) node = Node(val)
if not self.min_node: if not self.min_node:
self.min_node = node self.min_node = node
@ -91,13 +134,26 @@ class FibonacciHeap:
return node return node
def peek(self): def peek(self):
"""Return minimum value without removing it""" """
Returns the minimum value without removing it.
Returns:
The minimum value in the heap.
Raises:
IndexError: If the heap is empty.
"""
if not self.min_node: if not self.min_node:
raise IndexError("Heap is empty") raise IndexError("Heap is empty")
return self.min_node.val return self.min_node.val
def merge_heaps(self, other): def merge_heaps(self, other):
"""Merge another Fibonacci heap with this one""" """
Merges another Fibonacci heap into this one.
Args:
other: Another FibonacciHeap instance to merge with this one.
"""
if not other.min_node: if not other.min_node:
return return
if not self.min_node: if not self.min_node:
@ -115,7 +171,13 @@ class FibonacciHeap:
self.size += other.size self.size += other.size
def __link_trees(self, node1, node2): def __link_trees(self, node1, node2):
"""Link two trees of same degree""" """
Links two trees of same degree.
Args:
node1: First tree's root node.
node2: Second tree's root node.
"""
node1.remove() node1.remove()
if node2.child: if node2.child:
node2.child.add_sibling(node1) node2.child.add_sibling(node1)
@ -126,7 +188,15 @@ class FibonacciHeap:
node1.mark = False node1.mark = False
def delete_min(self): def delete_min(self):
"""Remove and return the minimum value""" """
Removes and returns the minimum value from the heap.
Returns:
The minimum value that was removed.
Raises:
IndexError: If the heap is empty.
"""
if not self.min_node: if not self.min_node:
raise IndexError("Heap is empty") raise IndexError("Heap is empty")
@ -156,8 +226,12 @@ class FibonacciHeap:
return min_val return min_val
def __consolidate(self): def __consolidate(self):
"""Consolidate trees after delete_min""" """
max_degree = int(self.size**0.5) + 1 Consolidates the trees in the heap after a delete_min operation.
This is an internal method that maintains the heap's structure.
"""
max_degree = int(self.size ** 0.5) + 1
degree_table = [None] * max_degree degree_table = [None] * max_degree
# Collect all roots # Collect all roots
@ -195,7 +269,16 @@ class FibonacciHeap:
self.min_node = degree_table[degree] self.min_node = degree_table[degree]
def decrease_key(self, node, new_val): def decrease_key(self, node, new_val):
"""Decrease the value of a node""" """
Decreases the value of a node.
Args:
node: The node whose value should be decreased.
new_val: The new value for the node.
Raises:
ValueError: If new value is greater than current value.
"""
if new_val > node.val: if new_val > node.val:
raise ValueError("New value is greater than current value") raise ValueError("New value is greater than current value")
@ -210,7 +293,19 @@ class FibonacciHeap:
self.min_node = node self.min_node = node
def __cut(self, node, parent): def __cut(self, node, parent):
"""Cut a node from its parent""" """
Cuts a node from its parent.
Args:
node: Node to be cut.
parent: Parent of the node to be cut.
""""""
Performs cascading cut operation.
Args:
node: Starting node for cascading cut.
"""
parent.degree -= 1 parent.degree -= 1
if parent.child == node: if parent.child == node:
parent.child = node.right if node.right != node else None parent.child = node.right if node.right != node else None
@ -222,8 +317,15 @@ class FibonacciHeap:
self.min_node.add_sibling(node) self.min_node.add_sibling(node)
def __cascading_cut(self, node): def __cascading_cut(self, node):
"""Perform cascading cut operation""" """
if parent := node.parent: Performs cascading cut operation.
Args:
node: Starting node for cascading cut.
"""
parent = node.parent
if parent:
if not node.mark: if not node.mark:
node.mark = True node.mark = True
else: else:
@ -231,7 +333,12 @@ class FibonacciHeap:
self.__cascading_cut(parent) self.__cascading_cut(parent)
def __str__(self): def __str__(self):
"""String representation of the heap""" """
Returns a string representation of the heap.
Returns:
str: A string showing the heap structure.
"""
if not self.min_node: if not self.min_node:
return "Empty heap" return "Empty heap"
@ -252,5 +359,4 @@ class FibonacciHeap:
if __name__ == "__main__": if __name__ == "__main__":
import doctest import doctest
doctest.testmod() doctest.testmod()