mirror of
https://github.com/TheAlgorithms/Python.git
synced 2025-02-07 01:50:55 +00:00
Added full implementation of fibonacci heap
This commit is contained in:
parent
144030aed1
commit
502953aa70
|
@ -1,8 +1,56 @@
|
|||
"""
|
||||
Fibonacci Heap Implementation in Python.
|
||||
|
||||
This module provides an implementation of a Fibonacci Heap, a data structure
|
||||
that supports a priority queue with efficient operations.
|
||||
Referenced from: https://en.wikipedia.org/wiki/Fibonacci_heap
|
||||
|
||||
Classes:
|
||||
- FibonacciHeapNode: Represents a node in the Fibonacci Heap.
|
||||
- FibonacciHeap: Represents the Fibonacci Heap itself.
|
||||
|
||||
Examples:
|
||||
>>> fh = FibonacciHeap()
|
||||
>>> n1 = fh.insert(10, "value1")
|
||||
>>> n2 = fh.insert(2, "value2")
|
||||
>>> n3 = fh.insert(15, "value3")
|
||||
>>> fh.find_min().key
|
||||
2
|
||||
>>> fh.decrease_key(n3, 1)
|
||||
>>> fh.find_min().key
|
||||
1
|
||||
>>> fh.extract_min().key
|
||||
1
|
||||
>>> fh.find_min().key
|
||||
2
|
||||
"""
|
||||
|
||||
import math
|
||||
|
||||
|
||||
class FibonacciHeapNode:
|
||||
"""
|
||||
Represents a node in the Fibonacci Heap.
|
||||
|
||||
Attributes:
|
||||
key (any): The key of the node.
|
||||
value (any): The value associated with the key.
|
||||
degree (int): The number of children of this node.
|
||||
parent (FibonacciHeapNode): The parent of this node.
|
||||
child (FibonacciHeapNode): The first child of this node.
|
||||
mark (bool): Whether this node has lost a child since it became a child of another node.
|
||||
next (FibonacciHeapNode): The next sibling in the circular doubly-linked list.
|
||||
prev (FibonacciHeapNode): The previous sibling in the circular doubly-linked list.
|
||||
"""
|
||||
|
||||
def __init__(self, key, value=None):
|
||||
"""
|
||||
Initializes a new Fibonacci Heap Node.
|
||||
|
||||
Args:
|
||||
key (any): The key of the node.
|
||||
value (any, optional): The value associated with the key. Defaults to None.
|
||||
"""
|
||||
self.key = key
|
||||
self.value = value
|
||||
self.degree = 0
|
||||
|
@ -13,6 +61,12 @@ class FibonacciHeapNode:
|
|||
self.prev = self
|
||||
|
||||
def add_child(self, node):
|
||||
"""
|
||||
Adds a child node to this node.
|
||||
|
||||
Args:
|
||||
node (FibonacciHeapNode): The child node to be added.
|
||||
"""
|
||||
if not self.child:
|
||||
self.child = node
|
||||
else:
|
||||
|
@ -24,6 +78,12 @@ class FibonacciHeapNode:
|
|||
self.degree += 1
|
||||
|
||||
def remove_child(self, node):
|
||||
"""
|
||||
Removes a child node from this node.
|
||||
|
||||
Args:
|
||||
node (FibonacciHeapNode): The child node to be removed.
|
||||
"""
|
||||
if node.next == node: # Single child
|
||||
self.child = None
|
||||
elif self.child == node:
|
||||
|
@ -35,14 +95,55 @@ class FibonacciHeapNode:
|
|||
|
||||
|
||||
class FibonacciHeap:
|
||||
"""
|
||||
Represents a Fibonacci Heap.
|
||||
|
||||
Attributes:
|
||||
min_node (FibonacciHeapNode): The node with the minimum key.
|
||||
total_nodes (int): The total number of nodes in the heap.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
"""
|
||||
Initializes an empty Fibonacci Heap.
|
||||
"""
|
||||
self.min_node = None
|
||||
self.total_nodes = 0
|
||||
|
||||
def is_empty(self):
|
||||
"""
|
||||
Checks if the heap is empty.
|
||||
|
||||
Returns:
|
||||
bool: True if the heap is empty, False otherwise.
|
||||
|
||||
Examples:
|
||||
>>> fh = FibonacciHeap()
|
||||
>>> fh.is_empty()
|
||||
True
|
||||
>>> n1 = fh.insert(5)
|
||||
>>> fh.is_empty()
|
||||
False
|
||||
"""
|
||||
return self.min_node is None
|
||||
|
||||
def insert(self, key, value=None):
|
||||
"""
|
||||
Inserts a new node into the heap.
|
||||
|
||||
Args:
|
||||
key (any): The key of the new node.
|
||||
value (any, optional): The value associated with the key. Defaults to None.
|
||||
|
||||
Returns:
|
||||
FibonacciHeapNode: The newly inserted node.
|
||||
|
||||
Examples:
|
||||
>>> fh = FibonacciHeap()
|
||||
>>> node = fh.insert(5, "value")
|
||||
>>> node.key
|
||||
5
|
||||
"""
|
||||
node = FibonacciHeapNode(key, value)
|
||||
self._merge_with_root_list(node)
|
||||
if not self.min_node or node.key < self.min_node.key:
|
||||
|
@ -51,11 +152,114 @@ class FibonacciHeap:
|
|||
return node
|
||||
|
||||
def find_min(self):
|
||||
"""
|
||||
Finds the node with the minimum key.
|
||||
|
||||
Returns:
|
||||
FibonacciHeapNode: The node with the minimum key.
|
||||
|
||||
Examples:
|
||||
>>> fh = FibonacciHeap()
|
||||
>>> n1 = fh.insert(10)
|
||||
>>> n2 = fh.insert(2)
|
||||
>>> fh.find_min().key
|
||||
2
|
||||
"""
|
||||
return self.min_node
|
||||
|
||||
def extract_min(self):
|
||||
"""
|
||||
Removes and returns the node with the minimum key.
|
||||
|
||||
Returns:
|
||||
FibonacciHeapNode: The node with the minimum key.
|
||||
|
||||
Examples:
|
||||
>>> fh = FibonacciHeap()
|
||||
>>> n1 = fh.insert(10)
|
||||
>>> n2 = fh.insert(2)
|
||||
>>> fh.extract_min().key
|
||||
2
|
||||
"""
|
||||
temp_min_node = self.min_node
|
||||
if temp_min_node:
|
||||
if temp_min_node.child:
|
||||
children = list(self._iterate(temp_min_node.child))
|
||||
for child in children:
|
||||
self._merge_with_root_list(child)
|
||||
child.parent = None
|
||||
self._remove_from_root_list(temp_min_node)
|
||||
if temp_min_node == temp_min_node.next:
|
||||
self.min_node = None
|
||||
else:
|
||||
self.min_node = temp_min_node.next
|
||||
self._consolidate()
|
||||
self.total_nodes -= 1
|
||||
return temp_min_node
|
||||
|
||||
def decrease_key(self, node, new_key):
|
||||
"""
|
||||
Decreases the key of a given node.
|
||||
|
||||
Args:
|
||||
node (FibonacciHeapNode): The node to decrease the key for.
|
||||
new_key (any): The new key value.
|
||||
|
||||
Raises:
|
||||
ValueError: If the new key is greater than the current key.
|
||||
|
||||
Examples:
|
||||
>>> fh = FibonacciHeap()
|
||||
>>> node = fh.insert(10)
|
||||
>>> fh.decrease_key(node, 5)
|
||||
>>> fh.find_min().key
|
||||
5
|
||||
"""
|
||||
if new_key > node.key:
|
||||
raise ValueError("New key is greater than current key")
|
||||
node.key = new_key
|
||||
temp_parent = node.parent
|
||||
if temp_parent and node.key < temp_parent.key:
|
||||
self._cut(node, temp_parent)
|
||||
self._cascading_cut(temp_parent)
|
||||
if node.key < self.min_node.key:
|
||||
self.min_node = node
|
||||
|
||||
def delete(self, x):
|
||||
"""
|
||||
Deletes a given node from the heap.
|
||||
|
||||
Args:
|
||||
x (FibonacciHeapNode): The node to be deleted.
|
||||
|
||||
Examples:
|
||||
>>> fh = FibonacciHeap()
|
||||
>>> node = fh.insert(10)
|
||||
>>> fh.delete(node)
|
||||
>>> fh.is_empty()
|
||||
True
|
||||
"""
|
||||
self.decrease_key(x, -math.inf)
|
||||
self.extract_min()
|
||||
|
||||
def union(self, other_heap):
|
||||
"""
|
||||
Merges another Fibonacci Heap into this heap.
|
||||
|
||||
Args:
|
||||
other_heap (FibonacciHeap): The other Fibonacci Heap to be merged.
|
||||
|
||||
Examples:
|
||||
>>> fh1 = FibonacciHeap()
|
||||
>>> fh2 = FibonacciHeap()
|
||||
>>> n1 = fh1.insert(10)
|
||||
>>> n2 = fh2.insert(5)
|
||||
>>> fh1.union(fh2)
|
||||
>>> fh1.find_min().key
|
||||
5
|
||||
"""
|
||||
if not other_heap.min_node:
|
||||
return self
|
||||
return
|
||||
if not self.min_node:
|
||||
self.min_node = other_heap.min_node
|
||||
else:
|
||||
|
@ -64,52 +268,13 @@ class FibonacciHeap:
|
|||
self.min_node = other_heap.min_node
|
||||
self.total_nodes += other_heap.total_nodes
|
||||
|
||||
def extract_min(self):
|
||||
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
|
||||
|
||||
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 delete(self, x):
|
||||
self.decrease_key(x, -math.inf)
|
||||
self.extract_min()
|
||||
|
||||
def _cut(self, x, y):
|
||||
y.remove_child(x)
|
||||
self._merge_with_root_list(x)
|
||||
x.mark = False
|
||||
|
||||
def _cascading_cut(self, y):
|
||||
if z := y.parent:
|
||||
if not y.mark:
|
||||
y.mark = True
|
||||
else:
|
||||
self._cut(y, z)
|
||||
self._cascading_cut(z)
|
||||
|
||||
def _merge_with_root_list(self, node):
|
||||
"""
|
||||
Merges a node into the root list.
|
||||
|
||||
Args:
|
||||
node (FibonacciHeapNode): The node to be merged.
|
||||
"""
|
||||
if not self.min_node:
|
||||
self.min_node = node
|
||||
else:
|
||||
|
@ -119,6 +284,12 @@ class FibonacciHeap:
|
|||
self.min_node.next = node
|
||||
|
||||
def _remove_from_root_list(self, node):
|
||||
"""
|
||||
Removes a node from the root list.
|
||||
|
||||
Args:
|
||||
node (FibonacciHeapNode): The node to be removed.
|
||||
"""
|
||||
if node.next == node:
|
||||
self.min_node = None
|
||||
else:
|
||||
|
@ -126,20 +297,23 @@ class FibonacciHeap:
|
|||
node.next.prev = node.prev
|
||||
|
||||
def _consolidate(self):
|
||||
"""
|
||||
Consolidates the heap by combining trees of the same degree.
|
||||
"""
|
||||
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
|
||||
for node in nodes:
|
||||
temp_node = node
|
||||
degree = temp_node.degree
|
||||
while array[degree]:
|
||||
array_node = array[degree]
|
||||
if temp_node.key > array_node.key:
|
||||
temp_node, array_node = array_node, temp_node
|
||||
self._link(array_node, temp_node)
|
||||
array[degree] = None
|
||||
degree += 1
|
||||
array[degree] = temp_node
|
||||
self.min_node = None
|
||||
for i in range(array_size):
|
||||
if array[i]:
|
||||
|
@ -150,29 +324,58 @@ class FibonacciHeap:
|
|||
if array[i].key < self.min_node.key:
|
||||
self.min_node = array[i]
|
||||
|
||||
def _link(self, y, x):
|
||||
self._remove_from_root_list(y)
|
||||
x.add_child(y)
|
||||
y.mark = False
|
||||
def _link(self, node_to_link, node_to_parent):
|
||||
"""
|
||||
Links two nodes by making one a child of the other.
|
||||
|
||||
Args:
|
||||
node_to_link (FibonacciHeapNode): The node to be linked as a child.
|
||||
node_to_parent (FibonacciHeapNode): The node to be the parent.
|
||||
"""
|
||||
self._remove_from_root_list(node_to_link)
|
||||
node_to_parent.add_child(node_to_link)
|
||||
node_to_link.mark = False
|
||||
|
||||
def _cut(self, node_to_cut, parent_node):
|
||||
"""
|
||||
Cuts a node from its parent and adds it to the root list.
|
||||
|
||||
Args:
|
||||
node_to_cut (FibonacciHeapNode): The node to be cut.
|
||||
parent_node (FibonacciHeapNode): The parent node.
|
||||
"""
|
||||
parent_node.remove_child(node_to_cut)
|
||||
self._merge_with_root_list(node_to_cut)
|
||||
node_to_cut.mark = False
|
||||
|
||||
def _cascading_cut(self, node_to_cut):
|
||||
"""
|
||||
Performs a cascading cut operation.
|
||||
|
||||
Args:
|
||||
node_to_cut (FibonacciHeapNode): The node to be cut recursively.
|
||||
"""
|
||||
temp_parent = node_to_cut.parent
|
||||
if temp_parent:
|
||||
if not node_to_cut.mark:
|
||||
node_to_cut.mark = True
|
||||
else:
|
||||
self._cut(node_to_cut, temp_parent)
|
||||
self._cascading_cut(temp_parent)
|
||||
|
||||
def _iterate(self, start):
|
||||
"""
|
||||
Iterates through a circular doubly linked list starting at a given node.
|
||||
|
||||
Args:
|
||||
start (FibonacciHeapNode): The starting node.
|
||||
|
||||
Yields:
|
||||
FibonacciHeapNode: The next node in the list.
|
||||
"""
|
||||
node = start
|
||||
while True:
|
||||
yield node
|
||||
node = node.next
|
||||
if node == start:
|
||||
break
|
||||
|
||||
|
||||
# Example usage
|
||||
if __name__ == "__main__":
|
||||
fh = FibonacciHeap()
|
||||
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