mirror of
https://github.com/TheAlgorithms/Python.git
synced 2024-11-23 21:11:08 +00:00
Retried commit with base fib heap implementation2
This commit is contained in:
parent
c4516d1dfc
commit
144030aed1
|
@ -1,357 +1,178 @@
|
||||||
"""
|
import math
|
||||||
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 FibonacciHeapNode:
|
||||||
"""
|
def __init__(self, key, value=None):
|
||||||
A node in a Fibonacci heap.
|
self.key = key
|
||||||
|
self.value = value
|
||||||
Args:
|
self.degree = 0
|
||||||
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):
|
|
||||||
self.val = val
|
|
||||||
self.parent = None
|
self.parent = None
|
||||||
self.child = None
|
self.child = None
|
||||||
self.left = self
|
|
||||||
self.right = self
|
|
||||||
self.degree = 0
|
|
||||||
self.mark = False
|
self.mark = False
|
||||||
|
self.next = self
|
||||||
def add_sibling(self, node):
|
self.prev = self
|
||||||
"""
|
|
||||||
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):
|
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:
|
if not self.child:
|
||||||
self.child = node
|
self.child = node
|
||||||
else:
|
else:
|
||||||
self.child.add_sibling(node)
|
node.prev = self.child
|
||||||
|
node.next = self.child.next
|
||||||
|
self.child.next.prev = node
|
||||||
|
self.child.next = node
|
||||||
|
node.parent = self
|
||||||
self.degree += 1
|
self.degree += 1
|
||||||
|
|
||||||
def remove(self):
|
def remove_child(self, node):
|
||||||
"""Removes this node from its sibling list."""
|
if node.next == node: # Single child
|
||||||
self.left.right = self.right
|
self.child = None
|
||||||
self.right.left = self.left
|
elif self.child == node:
|
||||||
|
self.child = node.next
|
||||||
|
node.prev.next = node.next
|
||||||
|
node.next.prev = node.prev
|
||||||
|
node.parent = None
|
||||||
|
self.degree -= 1
|
||||||
|
|
||||||
|
|
||||||
class FibonacciHeap:
|
class FibonacciHeap:
|
||||||
"""
|
|
||||||
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:
|
|
||||||
>>> heap = FibonacciHeap()
|
|
||||||
>>> node1 = heap.insert(3)
|
|
||||||
>>> node2 = heap.insert(2)
|
|
||||||
>>> node3 = heap.insert(15)
|
|
||||||
>>> heap.peek()
|
|
||||||
2
|
|
||||||
>>> heap.delete_min()
|
|
||||||
2
|
|
||||||
>>> heap.peek()
|
|
||||||
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.total_nodes = 0
|
||||||
|
|
||||||
def is_empty(self):
|
def is_empty(self):
|
||||||
"""
|
|
||||||
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, key, value=None):
|
||||||
"""
|
node = FibonacciHeapNode(key, value)
|
||||||
Inserts a new value into the heap.
|
self._merge_with_root_list(node)
|
||||||
|
if not self.min_node or node.key < self.min_node.key:
|
||||||
Args:
|
|
||||||
val: Value to insert.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
Node: The newly created node.
|
|
||||||
"""
|
|
||||||
node = Node(val)
|
|
||||||
if not self.min_node:
|
|
||||||
self.min_node = node
|
self.min_node = node
|
||||||
else:
|
self.total_nodes += 1
|
||||||
self.min_node.add_sibling(node)
|
|
||||||
if node.val < self.min_node.val:
|
|
||||||
self.min_node = node
|
|
||||||
self.size += 1
|
|
||||||
return node
|
return node
|
||||||
|
|
||||||
def peek(self):
|
def find_min(self):
|
||||||
"""
|
return self.min_node
|
||||||
Returns the minimum value without removing it.
|
|
||||||
|
|
||||||
Returns:
|
def union(self, other_heap):
|
||||||
The minimum value in the heap.
|
if not other_heap.min_node:
|
||||||
|
return self
|
||||||
Raises:
|
|
||||||
IndexError: If the heap is empty.
|
|
||||||
"""
|
|
||||||
if not self.min_node:
|
if not self.min_node:
|
||||||
raise IndexError("Heap is empty")
|
self.min_node = other_heap.min_node
|
||||||
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:
|
else:
|
||||||
# Merge root lists
|
self._merge_with_root_list(other_heap.min_node)
|
||||||
self.min_node.right.left = other.min_node.left
|
if other_heap.min_node.key < self.min_node.key:
|
||||||
other.min_node.left.right = self.min_node.right
|
self.min_node = other_heap.min_node
|
||||||
self.min_node.right = other.min_node
|
self.total_nodes += other_heap.total_nodes
|
||||||
other.min_node.left = self.min_node
|
|
||||||
|
|
||||||
if other.min_node.val < self.min_node.val:
|
def extract_min(self):
|
||||||
self.min_node = other.min_node
|
z = self.min_node
|
||||||
|
if z:
|
||||||
|
if z.child:
|
||||||
|
children = list(self._iterate(z.child))
|
||||||
|
for child in children:
|
||||||
|
self._merge_with_root_list(child)
|
||||||
|
child.parent = None
|
||||||
|
self._remove_from_root_list(z)
|
||||||
|
if z == z.next:
|
||||||
|
self.min_node = None
|
||||||
|
else:
|
||||||
|
self.min_node = z.next
|
||||||
|
self._consolidate()
|
||||||
|
self.total_nodes -= 1
|
||||||
|
return z
|
||||||
|
|
||||||
self.size += other.size
|
def decrease_key(self, x, new_key):
|
||||||
|
if new_key > x.key:
|
||||||
|
raise ValueError("New key is greater than current key")
|
||||||
|
x.key = new_key
|
||||||
|
y = x.parent
|
||||||
|
if y and x.key < y.key:
|
||||||
|
self._cut(x, y)
|
||||||
|
self._cascading_cut(y)
|
||||||
|
if x.key < self.min_node.key:
|
||||||
|
self.min_node = x
|
||||||
|
|
||||||
def __link_trees(self, node1, node2):
|
def delete(self, x):
|
||||||
"""
|
self.decrease_key(x, -math.inf)
|
||||||
Links two trees of same degree.
|
self.extract_min()
|
||||||
|
|
||||||
Args:
|
def _cut(self, x, y):
|
||||||
node1: First tree's root node.
|
y.remove_child(x)
|
||||||
node2: Second tree's root node.
|
self._merge_with_root_list(x)
|
||||||
"""
|
x.mark = False
|
||||||
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):
|
def _cascading_cut(self, y):
|
||||||
"""
|
if z := y.parent:
|
||||||
Removes and returns the minimum value from the heap.
|
if not y.mark:
|
||||||
|
y.mark = True
|
||||||
|
else:
|
||||||
|
self._cut(y, z)
|
||||||
|
self._cascading_cut(z)
|
||||||
|
|
||||||
Returns:
|
def _merge_with_root_list(self, node):
|
||||||
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")
|
self.min_node = node
|
||||||
|
else:
|
||||||
|
node.prev = self.min_node
|
||||||
|
node.next = self.min_node.next
|
||||||
|
self.min_node.next.prev = node
|
||||||
|
self.min_node.next = node
|
||||||
|
|
||||||
min_val = self.min_node.val
|
def _remove_from_root_list(self, node):
|
||||||
|
if node.next == node:
|
||||||
# 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
|
self.min_node = None
|
||||||
else:
|
else:
|
||||||
self.min_node.remove()
|
node.prev.next = node.next
|
||||||
self.min_node = self.min_node.right
|
node.next.prev = node.prev
|
||||||
self.__consolidate()
|
|
||||||
|
|
||||||
self.size -= 1
|
def _consolidate(self):
|
||||||
return min_val
|
array_size = int(math.log(self.total_nodes) * 2) + 1
|
||||||
|
array = [None] * array_size
|
||||||
|
nodes = list(self._iterate(self.min_node))
|
||||||
|
for w in nodes:
|
||||||
|
x = w
|
||||||
|
d = x.degree
|
||||||
|
while array[d]:
|
||||||
|
y = array[d]
|
||||||
|
if x.key > y.key:
|
||||||
|
x, y = y, x
|
||||||
|
self._link(y, x)
|
||||||
|
array[d] = None
|
||||||
|
d += 1
|
||||||
|
array[d] = x
|
||||||
|
self.min_node = None
|
||||||
|
for i in range(array_size):
|
||||||
|
if array[i]:
|
||||||
|
if not self.min_node:
|
||||||
|
self.min_node = array[i]
|
||||||
|
else:
|
||||||
|
self._merge_with_root_list(array[i])
|
||||||
|
if array[i].key < self.min_node.key:
|
||||||
|
self.min_node = array[i]
|
||||||
|
|
||||||
def __consolidate(self):
|
def _link(self, y, x):
|
||||||
"""
|
self._remove_from_root_list(y)
|
||||||
Consolidates the trees in the heap after a delete_min operation.
|
x.add_child(y)
|
||||||
|
y.mark = False
|
||||||
|
|
||||||
This is an internal method that maintains the heap's structure.
|
def _iterate(self, start):
|
||||||
"""
|
node = start
|
||||||
max_degree = int(self.size ** 0.5) + 1
|
|
||||||
degree_table = [None] * max_degree
|
|
||||||
|
|
||||||
# Collect all roots
|
|
||||||
roots = []
|
|
||||||
curr = self.min_node
|
|
||||||
while True:
|
while True:
|
||||||
roots.append(curr)
|
yield node
|
||||||
curr = curr.right
|
node = node.next
|
||||||
if curr == self.min_node:
|
if node == start:
|
||||||
break
|
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
|
|
||||||
else:
|
|
||||||
self.min_node.add_sibling(degree_table[degree])
|
|
||||||
if degree_table[degree].val < self.min_node.val:
|
|
||||||
self.min_node = degree_table[degree]
|
|
||||||
|
|
||||||
def decrease_key(self, node, new_val):
|
|
||||||
"""
|
|
||||||
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:
|
|
||||||
raise ValueError("New value is greater than current value")
|
|
||||||
|
|
||||||
node.val = new_val
|
|
||||||
parent = node.parent
|
|
||||||
|
|
||||||
if parent and node.val < parent.val:
|
|
||||||
self.__cut(node, parent)
|
|
||||||
self.__cascading_cut(parent)
|
|
||||||
|
|
||||||
if node.val < self.min_node.val:
|
|
||||||
self.min_node = node
|
|
||||||
|
|
||||||
def __cut(self, node, parent):
|
|
||||||
"""
|
|
||||||
Cuts a node from its parent
|
|
||||||
|
|
||||||
Args:
|
|
||||||
node: Node to be cut.
|
|
||||||
parent: Parent of the node to be cut.
|
|
||||||
"""
|
|
||||||
|
|
||||||
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.
|
|
||||||
"""
|
|
||||||
|
|
||||||
parent = node.parent
|
|
||||||
if parent:
|
|
||||||
if not node.mark:
|
|
||||||
node.mark = True
|
|
||||||
else:
|
|
||||||
self.__cut(node, parent)
|
|
||||||
self.__cascading_cut(parent)
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
"""
|
|
||||||
Returns a string representation of the heap.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
str: A string showing the heap structure.
|
|
||||||
"""
|
|
||||||
if not self.min_node:
|
|
||||||
return "Empty heap"
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
return "\n".join(print_tree(self.min_node))
|
|
||||||
|
|
||||||
|
|
||||||
|
# Example usage
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
import doctest
|
fh = FibonacciHeap()
|
||||||
doctest.testmod()
|
n1 = fh.insert(10, "value1")
|
||||||
|
n2 = fh.insert(2, "value2")
|
||||||
|
n3 = fh.insert(15, "value3")
|
||||||
|
|
||||||
|
print("Min:", fh.find_min().key) # Output: 2
|
||||||
|
fh.decrease_key(n3, 1)
|
||||||
|
print("Min after decrease key:", fh.find_min().key) # Output: 1
|
||||||
|
fh.extract_min()
|
||||||
|
print("Min after extract:", fh.find_min().key) # Output: 2
|
||||||
|
|
Loading…
Reference in New Issue
Block a user