mirror of
https://github.com/TheAlgorithms/Python.git
synced 2024-11-30 16:31:08 +00:00
Compare commits
4 Commits
9007583665
...
d20f6c756d
Author | SHA1 | Date | |
---|---|---|---|
|
d20f6c756d | ||
|
071ce716e4 | ||
|
a92936bd01 | ||
|
ea5a187b0a |
|
@ -1,444 +1,361 @@
|
||||||
from __future__ import annotations
|
"""
|
||||||
|
Fibonacci Heap
|
||||||
|
A more efficient priority queue implementation that provides amortized time bounds
|
||||||
|
that are better than those of the binary and binomial heaps.
|
||||||
|
reference: https://en.wikipedia.org/wiki/Fibonacci_heap
|
||||||
|
|
||||||
|
Operations supported:
|
||||||
|
- Insert: O(1) amortized
|
||||||
|
- Find minimum: O(1)
|
||||||
|
- Delete minimum: O(log n) amortized
|
||||||
|
- Decrease key: O(1) amortized
|
||||||
|
- Merge: O(1)
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
class Node:
|
class Node:
|
||||||
"""A node in the Fibonacci heap.
|
"""
|
||||||
|
A node in a Fibonacci heap.
|
||||||
|
|
||||||
Each node maintains references to its key, degree (number of children),
|
Args:
|
||||||
marked status, parent, child, and circular linked list references (left/right).
|
val: The value stored in the node.
|
||||||
|
|
||||||
Attributes:
|
Attributes:
|
||||||
key: The key value stored in the node
|
val: The value stored in the node.
|
||||||
degree: Number of children of the node
|
parent: Reference to parent node.
|
||||||
marked: Boolean indicating if the node is marked
|
child: Reference to one child node.
|
||||||
parent: Reference to parent node
|
left: Reference to left sibling.
|
||||||
child: Reference to one child node
|
right: Reference to right sibling.
|
||||||
left: Reference to left sibling in circular list
|
degree: Number of children.
|
||||||
right: Reference to right sibling in circular list
|
mark: Boolean indicating if node has lost a child.
|
||||||
|
|
||||||
Examples:
|
|
||||||
>>> node = Node(5)
|
|
||||||
>>> node.key
|
|
||||||
5
|
|
||||||
>>> node.degree
|
|
||||||
0
|
|
||||||
>>> node.marked
|
|
||||||
False
|
|
||||||
>>> node.left == node
|
|
||||||
True
|
|
||||||
>>> node.right == node
|
|
||||||
True
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, key: float | None) -> None:
|
def __init__(self, val):
|
||||||
self.key = key or None
|
self.val = val
|
||||||
self.degree = 0
|
self.parent = None
|
||||||
self.marked = False
|
self.child = None
|
||||||
self.parent = Node(None)
|
|
||||||
self.child = Node(None)
|
|
||||||
self.left = self
|
self.left = self
|
||||||
self.right = self
|
self.right = self
|
||||||
|
self.degree = 0
|
||||||
|
self.mark = False
|
||||||
|
|
||||||
|
def add_sibling(self, node):
|
||||||
|
"""
|
||||||
|
Adds a node as a sibling to the current node.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
node: The node to add as a sibling.
|
||||||
|
"""
|
||||||
|
node.left = self
|
||||||
|
node.right = self.right
|
||||||
|
self.right.left = node
|
||||||
|
self.right = node
|
||||||
|
|
||||||
|
def add_child(self, node):
|
||||||
|
"""
|
||||||
|
Adds a node as a child of the current node.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
node: The node to add 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):
|
||||||
|
"""Removes this node from its sibling list."""
|
||||||
|
self.left.right = self.right
|
||||||
|
self.right.left = self.left
|
||||||
|
|
||||||
|
|
||||||
class FibonacciHeap:
|
class FibonacciHeap:
|
||||||
"""Implementation of a Fibonacci heap using circular linked lists.
|
|
||||||
|
|
||||||
A Fibonacci heap is a collection of trees satisfying the min-heap property.
|
|
||||||
This implementation uses circular linked lists for both the root list and
|
|
||||||
child lists of nodes.
|
|
||||||
|
|
||||||
Attributes:
|
|
||||||
min_node: Reference to the node with minimum key
|
|
||||||
total_nodes: Total number of nodes in the heap
|
|
||||||
|
|
||||||
Reference: Introduction to Algorithms (CLRS) Chapter 19
|
|
||||||
https://en.wikipedia.org/wiki/Fibonacci_heap
|
|
||||||
|
|
||||||
Examples:
|
|
||||||
>>> heap = FibonacciHeap()
|
|
||||||
>>> heap.is_empty()
|
|
||||||
True
|
|
||||||
>>> node = heap.insert(3)
|
|
||||||
>>> node.key
|
|
||||||
3
|
|
||||||
>>> node2 = heap.insert(2)
|
|
||||||
>>> node2.key
|
|
||||||
2
|
|
||||||
>>> heap.find_min()
|
|
||||||
2
|
|
||||||
>>> heap.extract_min()
|
|
||||||
2
|
|
||||||
>>> heap.find_min()
|
|
||||||
3
|
|
||||||
"""
|
"""
|
||||||
|
A Fibonacci heap implementation providing
|
||||||
|
amortized efficient priority queue operations.
|
||||||
|
|
||||||
def __init__(self) -> None:
|
This implementation provides the following time complexities:
|
||||||
self.min_node = Node(None)
|
- Insert: O(1) amortized
|
||||||
self.total_nodes = 0
|
- Find minimum: O(1)
|
||||||
|
- Delete minimum: O(log n) amortized
|
||||||
def insert(self, key: float | None) -> Node:
|
- Decrease key: O(1) amortized
|
||||||
"""Insert a new key into the heap.
|
- Merge: O(1)
|
||||||
|
|
||||||
Args:
|
|
||||||
key: The key value to insert
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
Node: The newly created node
|
|
||||||
|
|
||||||
Examples:
|
|
||||||
>>> heap = FibonacciHeap()
|
|
||||||
>>> node = heap.insert(5)
|
|
||||||
>>> node.key
|
|
||||||
5
|
|
||||||
>>> heap.find_min()
|
|
||||||
5
|
|
||||||
>>> node2 = heap.insert(3)
|
|
||||||
>>> node2.key
|
|
||||||
3
|
|
||||||
>>> heap.find_min()
|
|
||||||
3
|
|
||||||
"""
|
|
||||||
new_node = Node(key)
|
|
||||||
if self.min_node is None:
|
|
||||||
self.min_node = new_node
|
|
||||||
else:
|
|
||||||
self._insert_into_circular_list(self.min_node, new_node)
|
|
||||||
if new_node.key < self.min_node.key:
|
|
||||||
self.min_node = new_node
|
|
||||||
self.total_nodes += 1
|
|
||||||
return new_node
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def _insert_into_circular_list(base_node: Node, node_to_insert: Node) -> Node:
|
|
||||||
"""Insert node into circular linked list.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
base_node: The reference node in the circular list
|
|
||||||
node_to_insert: The node to insert into the list
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
Node: The base node
|
|
||||||
|
|
||||||
Examples:
|
|
||||||
>>> heap = FibonacciHeap()
|
|
||||||
>>> node1 = Node(1)
|
|
||||||
>>> node2 = Node(2)
|
|
||||||
>>> result = heap._insert_into_circular_list(node1, node2)
|
|
||||||
>>> result == node1
|
|
||||||
True
|
|
||||||
>>> node1.right == node2
|
|
||||||
True
|
|
||||||
>>> node2.left == node1
|
|
||||||
True
|
|
||||||
"""
|
|
||||||
if base_node.key is None:
|
|
||||||
return node_to_insert
|
|
||||||
|
|
||||||
node_to_insert.right = base_node.right
|
|
||||||
node_to_insert.left = base_node
|
|
||||||
base_node.right.left = node_to_insert
|
|
||||||
base_node.right = node_to_insert
|
|
||||||
return base_node
|
|
||||||
|
|
||||||
def extract_min(self) -> float | None:
|
|
||||||
"""Remove and return the minimum key from the heap.
|
|
||||||
|
|
||||||
This operation removes the node with the minimum key from the heap,
|
|
||||||
adds all its children to the root list, and consolidates the heap
|
|
||||||
to maintain the Fibonacci heap properties. This is one of the more
|
|
||||||
complex operations with amortized time complexity of O(log n).
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
Node: The minimum key value that was removed,
|
|
||||||
or None if the heap is empty
|
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
>>> heap = FibonacciHeap()
|
>>> heap = FibonacciHeap()
|
||||||
>>> node1 = heap.insert(3)
|
>>> node1 = heap.insert(3)
|
||||||
>>> node2 = heap.insert(1)
|
>>> node2 = heap.insert(2)
|
||||||
>>> node3 = heap.insert(2)
|
>>> node3 = heap.insert(15)
|
||||||
>>> heap.extract_min() # Removes and returns 1
|
>>> heap.peek()
|
||||||
1
|
|
||||||
>>> heap.extract_min() # Removes and returns 2
|
|
||||||
2
|
2
|
||||||
>>> heap.extract_min() # Removes and returns 3
|
>>> heap.delete_min()
|
||||||
|
2
|
||||||
|
>>> heap.peek()
|
||||||
3
|
3
|
||||||
>>> heap.extract_min() # Heap is now empty
|
>>> other_heap = FibonacciHeap()
|
||||||
|
>>> node4 = other_heap.insert(1)
|
||||||
Note:
|
>>> heap.merge_heaps(other_heap)
|
||||||
This operation may trigger heap consolidation to maintain
|
>>> heap.peek()
|
||||||
the Fibonacci heap properties after removal of the minimum node.
|
1
|
||||||
"""
|
"""
|
||||||
if self.min_node is None:
|
|
||||||
return Node(None).key
|
|
||||||
|
|
||||||
min_node = self.min_node
|
def __init__(self):
|
||||||
|
"""Initializes an empty Fibonacci heap."""
|
||||||
|
self.min_node = None
|
||||||
|
self.size = 0
|
||||||
|
|
||||||
if min_node.child:
|
def is_empty(self):
|
||||||
current_child = min_node.child
|
"""
|
||||||
last_child = min_node.child.left
|
Checks if the heap is empty.
|
||||||
while True:
|
|
||||||
next_child = current_child.right
|
|
||||||
self._insert_into_circular_list(self.min_node, current_child)
|
|
||||||
current_child.parent.key = None
|
|
||||||
if current_child == last_child:
|
|
||||||
break
|
|
||||||
current_child = next_child
|
|
||||||
|
|
||||||
min_node.left.right = min_node.right
|
Returns:
|
||||||
min_node.right.left = min_node.left
|
bool: True if heap is empty, False otherwise.
|
||||||
|
"""
|
||||||
|
return self.min_node is None
|
||||||
|
|
||||||
if min_node == min_node.right:
|
def insert(self, val):
|
||||||
self.min_node.key = None
|
"""
|
||||||
|
Inserts a new value into the heap.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
val: Value to insert.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Node: The newly created node.
|
||||||
|
"""
|
||||||
|
node = Node(val)
|
||||||
|
if not self.min_node:
|
||||||
|
self.min_node = node
|
||||||
else:
|
else:
|
||||||
self.min_node = min_node.right
|
self.min_node.add_sibling(node)
|
||||||
self._consolidate()
|
if node.val < self.min_node.val:
|
||||||
|
self.min_node = node
|
||||||
|
self.size += 1
|
||||||
|
return node
|
||||||
|
|
||||||
self.total_nodes -= 1
|
def peek(self):
|
||||||
return min_node.key
|
|
||||||
|
|
||||||
def _consolidate(self) -> None:
|
|
||||||
"""Consolidate the heap after removing the minimum node.
|
|
||||||
|
|
||||||
This internal method maintains the Fibonacci heap properties by combining
|
|
||||||
trees of the same degree until no two roots have the same degree. This
|
|
||||||
process is key to maintaining the efficiency of the data structure.
|
|
||||||
|
|
||||||
The consolidation process works by:
|
|
||||||
1. Creating a temporary array indexed by tree degree
|
|
||||||
2. Processing each root node and combining trees of the same degree
|
|
||||||
3. Reconstructing the root list and finding the new minimum
|
|
||||||
|
|
||||||
Time complexity: O(log n) amortized
|
|
||||||
|
|
||||||
Note:
|
|
||||||
This is an internal method called by extract_min and should not be
|
|
||||||
called directly from outside the class.
|
|
||||||
"""
|
"""
|
||||||
max_degree = int(self.total_nodes**0.5) + 1
|
Returns the minimum value without removing it.
|
||||||
degree_table = [Node(None)] * max_degree
|
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The minimum value in the heap.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
IndexError: If the heap is empty.
|
||||||
|
"""
|
||||||
|
if not self.min_node:
|
||||||
|
raise IndexError("Heap is empty")
|
||||||
|
return self.min_node.val
|
||||||
|
|
||||||
|
def merge_heaps(self, other):
|
||||||
|
"""
|
||||||
|
Merges another Fibonacci heap into this one.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
other: Another FibonacciHeap instance to merge 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):
|
||||||
|
"""
|
||||||
|
Links two trees of same degree.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
node1: First tree's root node.
|
||||||
|
node2: Second tree's root node.
|
||||||
|
"""
|
||||||
|
node1.remove()
|
||||||
|
if node2.child:
|
||||||
|
node2.child.add_sibling(node1)
|
||||||
|
else:
|
||||||
|
node2.child = node1
|
||||||
|
node1.parent = node2
|
||||||
|
node2.degree += 1
|
||||||
|
node1.mark = False
|
||||||
|
|
||||||
|
def delete_min(self):
|
||||||
|
"""
|
||||||
|
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:
|
||||||
|
raise IndexError("Heap is empty")
|
||||||
|
|
||||||
|
min_val = self.min_node.val
|
||||||
|
|
||||||
|
# Add all children to root list
|
||||||
|
if self.min_node.child:
|
||||||
|
curr = self.min_node.child
|
||||||
|
while True:
|
||||||
|
next_node = curr.right
|
||||||
|
curr.parent = None
|
||||||
|
curr.mark = False
|
||||||
|
self.min_node.add_sibling(curr)
|
||||||
|
if curr.right == self.min_node.child:
|
||||||
|
break
|
||||||
|
curr = next_node
|
||||||
|
|
||||||
|
# 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()
|
||||||
|
|
||||||
|
self.size -= 1
|
||||||
|
return min_val
|
||||||
|
|
||||||
|
def __consolidate(self):
|
||||||
|
"""
|
||||||
|
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
|
||||||
|
|
||||||
|
# Collect all roots
|
||||||
roots = []
|
roots = []
|
||||||
if self.min_node:
|
curr = self.min_node
|
||||||
current_root = self.min_node
|
|
||||||
while True:
|
while True:
|
||||||
roots.append(current_root)
|
roots.append(curr)
|
||||||
if current_root.right == self.min_node:
|
curr = curr.right
|
||||||
|
if curr == self.min_node:
|
||||||
break
|
break
|
||||||
current_root = current_root.right
|
|
||||||
|
|
||||||
for current_root in roots:
|
# Consolidate trees
|
||||||
root_node = current_root
|
for root in roots:
|
||||||
current_degree = root_node.degree
|
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
|
||||||
|
|
||||||
while degree_table[current_degree] is not None:
|
# Find new minimum
|
||||||
other_root = degree_table[current_degree]
|
self.min_node = None
|
||||||
if root_node.key > other_root.key:
|
|
||||||
root_node, other_root = other_root, root_node
|
|
||||||
|
|
||||||
other_root.left.right = other_root.right
|
|
||||||
other_root.right.left = other_root.left
|
|
||||||
|
|
||||||
if root_node.child.key is None:
|
|
||||||
root_node.child = other_root
|
|
||||||
other_root.right = other_root
|
|
||||||
other_root.left = other_root
|
|
||||||
else:
|
|
||||||
self._insert_into_circular_list(root_node.child, other_root)
|
|
||||||
|
|
||||||
other_root.parent = root_node
|
|
||||||
root_node.degree += 1
|
|
||||||
other_root.marked = False
|
|
||||||
|
|
||||||
degree_table[current_degree] = Node(None)
|
|
||||||
current_degree += 1
|
|
||||||
|
|
||||||
degree_table[current_degree] = root_node
|
|
||||||
|
|
||||||
self.min_node.key = None
|
|
||||||
for degree in range(max_degree):
|
for degree in range(max_degree):
|
||||||
if degree_table[degree] is not None and (
|
if degree_table[degree]:
|
||||||
self.min_node is None or (degree_table[degree] < self.min_node.key)
|
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
|
||||||
|
else:
|
||||||
|
self.min_node.add_sibling(degree_table[degree])
|
||||||
|
if degree_table[degree].val < self.min_node.val:
|
||||||
self.min_node = degree_table[degree]
|
self.min_node = degree_table[degree]
|
||||||
|
|
||||||
def decrease_key(self, node: Node, new_key: float | None) -> None:
|
def decrease_key(self, node, new_val):
|
||||||
"""Decrease the key value of a given node.
|
"""
|
||||||
|
Decreases the value of a node.
|
||||||
This operation updates the key of a node to a new, smaller value and
|
|
||||||
maintains the min-heap property by potentially cutting the node from
|
|
||||||
its parent and performing cascading cuts up the tree.
|
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
node: The node whose key should be decreased
|
node: The node whose value should be decreased.
|
||||||
new_key: The new key value, must be smaller than the current key
|
new_val: The new value for the node.
|
||||||
|
|
||||||
Example:
|
Raises:
|
||||||
>>> heap = FibonacciHeap()
|
ValueError: If new value is greater than current value.
|
||||||
>>> node1 = heap.insert(5)
|
|
||||||
>>> heap.decrease_key(node, 3)
|
|
||||||
>>> node.key
|
|
||||||
3
|
|
||||||
>>> heap.find_min()
|
|
||||||
3
|
|
||||||
>>> heap.decrease_key(node, 1)
|
|
||||||
>>> node.key
|
|
||||||
1
|
|
||||||
>>> heap.find_min()
|
|
||||||
1
|
|
||||||
"""
|
"""
|
||||||
if new_key > node.key:
|
if new_val > node.val:
|
||||||
raise ValueError("New key is greater than current key")
|
raise ValueError("New value is greater than current value")
|
||||||
|
|
||||||
node.key = new_key
|
node.val = new_val
|
||||||
parent_node = node.parent
|
parent = node.parent
|
||||||
|
|
||||||
if parent_node.key is not None and node.key < parent_node.key:
|
if parent and node.val < parent.val:
|
||||||
self._cut(node, parent_node)
|
self.__cut(node, parent)
|
||||||
self._cascading_cut(parent_node)
|
self.__cascading_cut(parent)
|
||||||
|
|
||||||
if node.key < self.min_node.key:
|
if node.val < self.min_node.val:
|
||||||
self.min_node = node
|
self.min_node = node
|
||||||
|
|
||||||
def _cut(self, child_node: Node, parent_node: Node) -> None:
|
def __cut(self, node, parent):
|
||||||
"""Cut a node from its parent and add it to the root list.
|
"""
|
||||||
|
Cuts a node from its parent.
|
||||||
This is a helper method used in decrease_key operations. When a node's key
|
|
||||||
becomes smaller than its parent's key, it needs to be cut from its parent
|
|
||||||
and added to the root list to maintain the min-heap property.
|
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
child_node: The node to be cut from its parent
|
node: Node to be cut.
|
||||||
parent_node: The parent node from which to cut
|
parent: Parent of the node to be cut.
|
||||||
|
""" """
|
||||||
|
Performs cascading cut operation.
|
||||||
|
|
||||||
Note:
|
Args:
|
||||||
This is an internal method that maintains heap properties during
|
node: Starting node for cascading cut.
|
||||||
decrease_key operations. It should not be called directly from
|
|
||||||
outside the class.
|
|
||||||
"""
|
"""
|
||||||
if child_node.right == child_node:
|
|
||||||
parent_node.child = Node(None)
|
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):
|
||||||
|
"""
|
||||||
|
Performs cascading cut operation.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
node: Starting node for cascading cut.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if parent := node.parent:
|
||||||
|
if not node.mark:
|
||||||
|
node.mark = True
|
||||||
else:
|
else:
|
||||||
parent_node.child = child_node.right
|
self.__cut(node, parent)
|
||||||
child_node.right.left = child_node.left
|
self.__cascading_cut(parent)
|
||||||
child_node.left.right = child_node.right
|
|
||||||
|
|
||||||
parent_node.degree -= 1
|
def __str__(self):
|
||||||
|
|
||||||
self._insert_into_circular_list(self.min_node, child_node)
|
|
||||||
child_node.parent = Node(None)
|
|
||||||
child_node.marked = False
|
|
||||||
|
|
||||||
def _cascading_cut(self, current_node: Node) -> None:
|
|
||||||
"""Perform cascading cut operation.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
current_node: The node to start cascading cut from
|
|
||||||
"""
|
"""
|
||||||
if (parent_node := current_node.parent) is not None:
|
Returns a string representation of the heap.
|
||||||
if not current_node.marked:
|
|
||||||
current_node.marked = True
|
|
||||||
else:
|
|
||||||
self._cut(current_node, parent_node)
|
|
||||||
self._cascading_cut(parent_node)
|
|
||||||
|
|
||||||
def delete(self, node: Node) -> None:
|
|
||||||
"""Delete a node from the heap.
|
|
||||||
|
|
||||||
This operation removes a given node from the heap by first decreasing
|
|
||||||
its key to negative infinity (making it the minimum) and then extracting
|
|
||||||
the minimum.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
node: The node to be deleted from the heap
|
|
||||||
|
|
||||||
Example:
|
|
||||||
>>> heap = FibonacciHeap()
|
|
||||||
>>> node1 = heap.insert(3)
|
|
||||||
>>> node2 = heap.insert(2)
|
|
||||||
>>> heap.delete(node1)
|
|
||||||
>>> heap.find_min()
|
|
||||||
2
|
|
||||||
>>> heap.total_nodes
|
|
||||||
1
|
|
||||||
|
|
||||||
Note:
|
|
||||||
This operation has an amortized time complexity of O(log n)
|
|
||||||
as it combines decrease_key and extract_min operations.
|
|
||||||
"""
|
|
||||||
self.decrease_key(node, float("-inf"))
|
|
||||||
self.extract_min()
|
|
||||||
|
|
||||||
def find_min(self) -> float | None:
|
|
||||||
"""Return the minimum key without removing it from the heap.
|
|
||||||
|
|
||||||
This operation provides quick access to the minimum key in the heap
|
|
||||||
without modifying the heap structure.
|
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
float | None: The minimum key value, or None if the heap is empty
|
str: A string showing the heap structure.
|
||||||
|
|
||||||
Example:
|
|
||||||
>>> heap = FibonacciHeap()
|
|
||||||
>>> heap.find_min() is None
|
|
||||||
True
|
|
||||||
>>> node1 = heap.insert(3)
|
|
||||||
>>> heap.find_min()
|
|
||||||
3
|
|
||||||
"""
|
"""
|
||||||
return self.min_node.key if self.min_node else Node(None).key
|
if not self.min_node:
|
||||||
|
return "Empty heap"
|
||||||
|
|
||||||
def is_empty(self) -> bool:
|
def print_tree(node, level=0):
|
||||||
"""Check if heap is empty.
|
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
|
||||||
|
|
||||||
Returns:
|
return "\n".join(print_tree(self.min_node))
|
||||||
bool: True if heap is empty, False otherwise
|
|
||||||
|
|
||||||
Examples:
|
|
||||||
>>> heap = FibonacciHeap()
|
|
||||||
>>> heap.is_empty()
|
|
||||||
True
|
|
||||||
>>> node = heap.insert(1)
|
|
||||||
>>> heap.is_empty()
|
|
||||||
False
|
|
||||||
"""
|
|
||||||
return self.min_node.key is None
|
|
||||||
|
|
||||||
def merge(self, other_heap: FibonacciHeap) -> None:
|
|
||||||
"""Merge another Fibonacci heap into this one.
|
|
||||||
|
|
||||||
This operation combines two Fibonacci heaps by concatenating their
|
|
||||||
root lists and updating the minimum pointer if necessary. The other
|
|
||||||
heap is effectively consumed in this process.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
other_heap: Another FibonacciHeap instance to merge into this one
|
|
||||||
|
|
||||||
Example:
|
|
||||||
>>> heap1 = FibonacciHeap()
|
|
||||||
>>> node1 = heap1.insert(3)
|
|
||||||
>>> heap2 = FibonacciHeap()
|
|
||||||
>>> node2 = heap2.insert(2)
|
|
||||||
>>> heap1.merge(heap2)
|
|
||||||
>>> heap1.find_min()
|
|
||||||
2
|
|
||||||
>>> heap1.total_nodes
|
|
||||||
2
|
|
||||||
"""
|
|
||||||
if other_heap.min_node.key is None:
|
|
||||||
return
|
|
||||||
if self.min_node.key is None:
|
|
||||||
self.min_node = other_heap.min_node
|
|
||||||
else:
|
|
||||||
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.total_nodes += other_heap.total_nodes
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|
Loading…
Reference in New Issue
Block a user