From a71d17a265abcd1f5fb465bae7a204e79b5e7372 Mon Sep 17 00:00:00 2001 From: mcawezome <20085898@tafe.wa.edu.au> Date: Wed, 13 Nov 2024 16:36:58 +0800 Subject: [PATCH 01/15] Added an implementation of fibonacci heap. --- data_structures/heap/fibonacci_heap.py | 443 +++++++++++++++++++++++++ 1 file changed, 443 insertions(+) create mode 100644 data_structures/heap/fibonacci_heap.py diff --git a/data_structures/heap/fibonacci_heap.py b/data_structures/heap/fibonacci_heap.py new file mode 100644 index 000000000000..7ec4ce1c78e6 --- /dev/null +++ b/data_structures/heap/fibonacci_heap.py @@ -0,0 +1,443 @@ +class Node: + """A node in the Fibonacci heap. + + Each node maintains references to its key, degree (number of children), + marked status, parent, child, and circular linked list references (left/right). + + Attributes: + key: The key value stored in the node + degree: Number of children of the node + marked: Boolean indicating if the node is marked + parent: Reference to parent node + child: Reference to one child node + left: Reference to left sibling in circular list + right: Reference to right sibling in circular list + + 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): + self.key = key + self.degree = 0 + self.marked = False + self.parent = None + self.child = None + self.left = self + self.right = self + + +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 + """ + + def __init__(self): + self.min_node = None + self.total_nodes = 0 + + def insert(self, key): + """Insert a new key into the heap. + + 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 + + def _insert_into_circular_list(self, base_node, node_to_insert): + """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 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): + """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: + >>> heap = FibonacciHeap() + >>> node1 = heap.insert(3) + >>> node2 = heap.insert(1) + >>> node3 = heap.insert(2) + >>> heap.extract_min() # Removes and returns 1 + 1 + >>> heap.extract_min() # Removes and returns 2 + 2 + >>> heap.extract_min() # Removes and returns 3 + 3 + >>> heap.extract_min() # Heap is now empty + + Note: + This operation may trigger heap consolidation to maintain + the Fibonacci heap properties after removal of the minimum node. + """ + if self.min_node is None: + return None + + min_node = self.min_node + + if min_node.child: + current_child = min_node.child + last_child = min_node.child.left + while True: + next_child = current_child.right + self._insert_into_circular_list(self.min_node, current_child) + current_child.parent = None + if current_child == last_child: + break + current_child = next_child + + min_node.left.right = min_node.right + min_node.right.left = min_node.left + + if min_node == min_node.right: + self.min_node = None + else: + self.min_node = min_node.right + self._consolidate() + + self.total_nodes -= 1 + return min_node.key + + def _consolidate(self): + """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 + degree_table = [None] * max_degree + + roots = [] + if self.min_node: + current_root = self.min_node + while True: + roots.append(current_root) + if current_root.right == self.min_node: + break + current_root = current_root.right + + for current_root in roots: + root_node = current_root + current_degree = root_node.degree + + while degree_table[current_degree] is not None: + other_root = degree_table[current_degree] + 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 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] = None + current_degree += 1 + + degree_table[current_degree] = root_node + + self.min_node = None + for degree in range(max_degree): + if degree_table[degree] is not None and ( + self.min_node is None or (degree_table[degree].key < self.min_node.key) + ): + self.min_node = degree_table[degree] + + def decrease_key(self, node, new_key): + """Decrease the key value of a given 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: + node: The node whose key should be decreased + new_key: The new key value, must be smaller than the current key + + Example: + >>> heap = FibonacciHeap() + >>> node = 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: + raise ValueError("New key is greater than current key") + + node.key = new_key + parent_node = node.parent + + if parent_node is not None and node.key < parent_node.key: + self._cut(node, parent_node) + self._cascading_cut(parent_node) + + if node.key < self.min_node.key: + self.min_node = node + + def _cut(self, child_node, parent_node): + """Cut a node from its parent and add it to the root list. + + 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: + child_node: The node to be cut from its parent + parent_node: The parent node from which to cut + + Note: + This is an internal method that maintains heap properties during + decrease_key operations. It should not be called directly from + outside the class. + """ + if child_node.right == child_node: + parent_node.child = None + else: + parent_node.child = child_node.right + child_node.right.left = child_node.left + child_node.left.right = child_node.right + + parent_node.degree -= 1 + + self._insert_into_circular_list(self.min_node, child_node) + child_node.parent = None + child_node.marked = False + + def _cascading_cut(self, current_node): + """Perform cascading cut operation. + + Args: + current_node: The node to start cascading cut from + """ + if (parent_node := current_node.parent) is not None: + 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): + """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): + """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: + float | None: The minimum key value, or None if the heap is empty + + 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 None + + def is_empty(self): + """Check if heap is empty. + + Returns: + 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 is None + + def merge(self, other_heap): + """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 is None: + return + if self.min_node 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__": + import doctest + + doctest.testmod() From a21a6240059cafe4d51c787880dad90053a519b4 Mon Sep 17 00:00:00 2001 From: mcawezome <20085898@tafe.wa.edu.au> Date: Sun, 17 Nov 2024 10:58:59 +0800 Subject: [PATCH 02/15] Added type hints to satisfy automated checks --- data_structures/heap/fibonacci_heap.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/data_structures/heap/fibonacci_heap.py b/data_structures/heap/fibonacci_heap.py index 7ec4ce1c78e6..f7f1017ac504 100644 --- a/data_structures/heap/fibonacci_heap.py +++ b/data_structures/heap/fibonacci_heap.py @@ -27,7 +27,7 @@ class Node: True """ - def __init__(self, key): + def __init__(self, key) -> None: self.key = key self.degree = 0 self.marked = False @@ -69,11 +69,11 @@ class FibonacciHeap: 3 """ - def __init__(self): + def __init__(self) -> None: self.min_node = None self.total_nodes = 0 - def insert(self, key): + def insert(self, key) -> Node: """Insert a new key into the heap. Args: @@ -105,7 +105,7 @@ def insert(self, key): self.total_nodes += 1 return new_node - def _insert_into_circular_list(self, base_node, node_to_insert): + def _insert_into_circular_list(self, base_node, node_to_insert) -> Node: """Insert node into circular linked list. Args: @@ -136,7 +136,7 @@ def _insert_into_circular_list(self, base_node, node_to_insert): base_node.right = node_to_insert return base_node - def extract_min(self): + def extract_min(self) -> Node: """Remove and return the minimum key from the heap. This operation removes the node with the minimum key from the heap, @@ -325,7 +325,7 @@ def _cut(self, child_node, parent_node): child_node.parent = None child_node.marked = False - def _cascading_cut(self, current_node): + def _cascading_cut(self, current_node) -> None: """Perform cascading cut operation. Args: @@ -338,7 +338,7 @@ def _cascading_cut(self, current_node): self._cut(current_node, parent_node) self._cascading_cut(parent_node) - def delete(self, node): + def delete(self, node) -> None: """Delete a node from the heap. This operation removes a given node from the heap by first decreasing @@ -365,7 +365,7 @@ def delete(self, node): self.decrease_key(node, float("-inf")) self.extract_min() - def find_min(self): + def find_min(self) -> Any: """Return the minimum key without removing it from the heap. This operation provides quick access to the minimum key in the heap @@ -384,7 +384,7 @@ def find_min(self): """ return self.min_node.key if self.min_node else None - def is_empty(self): + def is_empty(self) -> bool: """Check if heap is empty. Returns: @@ -400,7 +400,7 @@ def is_empty(self): """ return self.min_node is None - def merge(self, other_heap): + def merge(self, other_heap) -> None: """Merge another Fibonacci heap into this one. This operation combines two Fibonacci heaps by concatenating their From 2e35261d091113645cc9dbe0958094c48c29378c Mon Sep 17 00:00:00 2001 From: mcawezome <20085898@tafe.wa.edu.au> Date: Sun, 17 Nov 2024 11:09:56 +0800 Subject: [PATCH 03/15] Fixed issue with None type for nodes --- data_structures/heap/fibonacci_heap.py | 44 +++++++++++++------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/data_structures/heap/fibonacci_heap.py b/data_structures/heap/fibonacci_heap.py index f7f1017ac504..7ed86b6b3753 100644 --- a/data_structures/heap/fibonacci_heap.py +++ b/data_structures/heap/fibonacci_heap.py @@ -28,11 +28,11 @@ class Node: """ def __init__(self, key) -> None: - self.key = key + self.key = key or None self.degree = 0 self.marked = False - self.parent = None - self.child = None + self.parent = Node(None) + self.child = Node(None) self.left = self self.right = self @@ -70,7 +70,7 @@ class FibonacciHeap: """ def __init__(self) -> None: - self.min_node = None + self.min_node = Node(None) self.total_nodes = 0 def insert(self, key) -> Node: @@ -127,7 +127,7 @@ def _insert_into_circular_list(self, base_node, node_to_insert) -> Node: >>> node2.left == node1 True """ - if base_node is None: + if base_node.key is None: return node_to_insert node_to_insert.right = base_node.right @@ -166,7 +166,7 @@ def extract_min(self) -> Node: the Fibonacci heap properties after removal of the minimum node. """ if self.min_node is None: - return None + return Node(None) min_node = self.min_node @@ -176,7 +176,7 @@ def extract_min(self) -> Node: while True: next_child = current_child.right self._insert_into_circular_list(self.min_node, current_child) - current_child.parent = None + current_child.parent.key = None if current_child == last_child: break current_child = next_child @@ -185,7 +185,7 @@ def extract_min(self) -> Node: min_node.right.left = min_node.left if min_node == min_node.right: - self.min_node = None + self.min_node.key = None else: self.min_node = min_node.right self._consolidate() @@ -212,7 +212,7 @@ def _consolidate(self): called directly from outside the class. """ max_degree = int(self.total_nodes**0.5) + 1 - degree_table = [None] * max_degree + degree_table = [Node(None)] * max_degree roots = [] if self.min_node: @@ -235,7 +235,7 @@ def _consolidate(self): other_root.left.right = other_root.right other_root.right.left = other_root.left - if root_node.child is None: + if root_node.child.key is None: root_node.child = other_root other_root.right = other_root other_root.left = other_root @@ -246,15 +246,15 @@ def _consolidate(self): root_node.degree += 1 other_root.marked = False - degree_table[current_degree] = None + degree_table[current_degree] = Node(None) current_degree += 1 degree_table[current_degree] = root_node - self.min_node = None + self.min_node.key = None for degree in range(max_degree): if degree_table[degree] is not None and ( - self.min_node is None or (degree_table[degree].key < self.min_node.key) + self.min_node is None or (degree_table[degree] < self.min_node.key) ): self.min_node = degree_table[degree] @@ -271,7 +271,7 @@ def decrease_key(self, node, new_key): Example: >>> heap = FibonacciHeap() - >>> node = heap.insert(5) + >>> node1 = heap.insert(5) >>> heap.decrease_key(node, 3) >>> node.key 3 @@ -289,7 +289,7 @@ def decrease_key(self, node, new_key): node.key = new_key parent_node = node.parent - if parent_node is not None and node.key < parent_node.key: + if parent_node.key is not None and node.key < parent_node.key: self._cut(node, parent_node) self._cascading_cut(parent_node) @@ -313,7 +313,7 @@ def _cut(self, child_node, parent_node): outside the class. """ if child_node.right == child_node: - parent_node.child = None + parent_node.child = Node(None) else: parent_node.child = child_node.right child_node.right.left = child_node.left @@ -322,7 +322,7 @@ def _cut(self, child_node, parent_node): parent_node.degree -= 1 self._insert_into_circular_list(self.min_node, child_node) - child_node.parent = None + child_node.parent = Node(None) child_node.marked = False def _cascading_cut(self, current_node) -> None: @@ -365,7 +365,7 @@ def delete(self, node) -> None: self.decrease_key(node, float("-inf")) self.extract_min() - def find_min(self) -> Any: + def find_min(self) -> float: """Return the minimum key without removing it from the heap. This operation provides quick access to the minimum key in the heap @@ -382,7 +382,7 @@ def find_min(self) -> Any: >>> heap.find_min() 3 """ - return self.min_node.key if self.min_node else None + return self.min_node.key if self.min_node else Node(None) def is_empty(self) -> bool: """Check if heap is empty. @@ -398,7 +398,7 @@ def is_empty(self) -> bool: >>> heap.is_empty() False """ - return self.min_node is None + return self.min_node.key is None def merge(self, other_heap) -> None: """Merge another Fibonacci heap into this one. @@ -421,9 +421,9 @@ def merge(self, other_heap) -> None: >>> heap1.total_nodes 2 """ - if other_heap.min_node is None: + if other_heap.min_node.key is None: return - if self.min_node is None: + 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 From fe95a31fdfc8abf1e35879793e7540cdb0cc5199 Mon Sep 17 00:00:00 2001 From: mcawezome <20085898@tafe.wa.edu.au> Date: Sun, 17 Nov 2024 11:14:45 +0800 Subject: [PATCH 04/15] Minor fixes for type checking --- data_structures/heap/fibonacci_heap.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/data_structures/heap/fibonacci_heap.py b/data_structures/heap/fibonacci_heap.py index 7ed86b6b3753..ee7c99cb9391 100644 --- a/data_structures/heap/fibonacci_heap.py +++ b/data_structures/heap/fibonacci_heap.py @@ -136,7 +136,7 @@ def _insert_into_circular_list(self, base_node, node_to_insert) -> Node: base_node.right = node_to_insert return base_node - def extract_min(self) -> Node: + def extract_min(self) -> float: """Remove and return the minimum key from the heap. This operation removes the node with the minimum key from the heap, @@ -166,7 +166,7 @@ def extract_min(self) -> Node: the Fibonacci heap properties after removal of the minimum node. """ if self.min_node is None: - return Node(None) + return Node(None).key min_node = self.min_node @@ -382,7 +382,7 @@ def find_min(self) -> float: >>> heap.find_min() 3 """ - return self.min_node.key if self.min_node else Node(None) + return self.min_node.key if self.min_node else Node(None).key def is_empty(self) -> bool: """Check if heap is empty. From 369994d334086e489c81bd81d0ac6f43a18c9e3a Mon Sep 17 00:00:00 2001 From: mcawezome <20085898@tafe.wa.edu.au> Date: Sun, 17 Nov 2024 11:30:25 +0800 Subject: [PATCH 05/15] Added type hints and warning fixes --- data_structures/heap/fibonacci_heap.py | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/data_structures/heap/fibonacci_heap.py b/data_structures/heap/fibonacci_heap.py index ee7c99cb9391..d651948f74fa 100644 --- a/data_structures/heap/fibonacci_heap.py +++ b/data_structures/heap/fibonacci_heap.py @@ -1,3 +1,6 @@ +from __future__ import annotations + + class Node: """A node in the Fibonacci heap. @@ -27,7 +30,7 @@ class Node: True """ - def __init__(self, key) -> None: + def __init__(self, key: float | None) -> None: self.key = key or None self.degree = 0 self.marked = False @@ -73,7 +76,7 @@ def __init__(self) -> None: self.min_node = Node(None) self.total_nodes = 0 - def insert(self, key) -> Node: + def insert(self, key: float | None) -> Node: """Insert a new key into the heap. Args: @@ -105,7 +108,8 @@ def insert(self, key) -> Node: self.total_nodes += 1 return new_node - def _insert_into_circular_list(self, base_node, node_to_insert) -> Node: + @staticmethod + def _insert_into_circular_list(base_node: Node, node_to_insert: Node) -> Node: """Insert node into circular linked list. Args: @@ -136,7 +140,7 @@ def _insert_into_circular_list(self, base_node, node_to_insert) -> Node: base_node.right = node_to_insert return base_node - def extract_min(self) -> float: + 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, @@ -193,7 +197,7 @@ def extract_min(self) -> float: self.total_nodes -= 1 return min_node.key - def _consolidate(self): + def _consolidate(self) -> None: """Consolidate the heap after removing the minimum node. This internal method maintains the Fibonacci heap properties by combining @@ -258,7 +262,7 @@ def _consolidate(self): ): self.min_node = degree_table[degree] - def decrease_key(self, node, new_key): + def decrease_key(self, node: Node, new_key: float | None) -> None: """Decrease the key value of a given node. This operation updates the key of a node to a new, smaller value and @@ -296,7 +300,7 @@ def decrease_key(self, node, new_key): if node.key < self.min_node.key: self.min_node = node - def _cut(self, child_node, parent_node): + def _cut(self, child_node: Node, parent_node: Node) -> None: """Cut a node from its parent and add it to the root list. This is a helper method used in decrease_key operations. When a node's key @@ -325,7 +329,7 @@ def _cut(self, child_node, parent_node): child_node.parent = Node(None) child_node.marked = False - def _cascading_cut(self, current_node) -> None: + def _cascading_cut(self, current_node: Node) -> None: """Perform cascading cut operation. Args: @@ -338,7 +342,7 @@ def _cascading_cut(self, current_node) -> None: self._cut(current_node, parent_node) self._cascading_cut(parent_node) - def delete(self, node) -> None: + def delete(self, node: Node) -> None: """Delete a node from the heap. This operation removes a given node from the heap by first decreasing @@ -365,7 +369,7 @@ def delete(self, node) -> None: self.decrease_key(node, float("-inf")) self.extract_min() - def find_min(self) -> float: + 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 @@ -400,7 +404,7 @@ def is_empty(self) -> bool: """ return self.min_node.key is None - def merge(self, other_heap) -> None: + def merge(self, other_heap: FibonacciHeap) -> None: """Merge another Fibonacci heap into this one. This operation combines two Fibonacci heaps by concatenating their From ea5a187b0aed078fd287b730647c90400ba0412b Mon Sep 17 00:00:00 2001 From: mcawezome <20085898@tafe.wa.edu.au> Date: Mon, 18 Nov 2024 11:03:04 +0800 Subject: [PATCH 06/15] Rewrote entire file to pass checks --- data_structures/heap/fibonacci_heap.py | 623 +++++++++---------------- 1 file changed, 216 insertions(+), 407 deletions(-) diff --git a/data_structures/heap/fibonacci_heap.py b/data_structures/heap/fibonacci_heap.py index d651948f74fa..28cd48bf23ad 100644 --- a/data_structures/heap/fibonacci_heap.py +++ b/data_structures/heap/fibonacci_heap.py @@ -1,444 +1,253 @@ -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. + +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: - """A node in the Fibonacci heap. - - Each node maintains references to its key, degree (number of children), - marked status, parent, child, and circular linked list references (left/right). - - Attributes: - key: The key value stored in the node - degree: Number of children of the node - marked: Boolean indicating if the node is marked - parent: Reference to parent node - child: Reference to one child node - left: Reference to left sibling in circular list - right: Reference to right sibling in circular list - - Examples: - >>> node = Node(5) - >>> node.key - 5 - >>> node.degree - 0 - >>> node.marked - False - >>> node.left == node - True - >>> node.right == node - True + """ + 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) """ - def __init__(self, key: float | None) -> None: - self.key = key or None - self.degree = 0 - self.marked = False - self.parent = Node(None) - self.child = Node(None) + def __init__(self, val): + self.val = val + self.parent = None + self.child = None self.left = self self.right = self + 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 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 + """ + 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 """ - def __init__(self) -> None: - self.min_node = Node(None) - self.total_nodes = 0 - - def insert(self, key: float | None) -> Node: - """Insert a new key into the heap. - - 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: - >>> heap = FibonacciHeap() - >>> node1 = heap.insert(3) - >>> node2 = heap.insert(1) - >>> node3 = heap.insert(2) - >>> heap.extract_min() # Removes and returns 1 - 1 - >>> heap.extract_min() # Removes and returns 2 - 2 - >>> heap.extract_min() # Removes and returns 3 - 3 - >>> heap.extract_min() # Heap is now empty - - Note: - This operation may trigger heap consolidation to maintain - the Fibonacci heap properties after removal of the minimum node. - """ - if self.min_node is None: - return Node(None).key - - min_node = self.min_node - - if min_node.child: - current_child = min_node.child - last_child = min_node.child.left - 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 + def __init__(self): + self.min_node = None + self.size = 0 - min_node.left.right = min_node.right - min_node.right.left = min_node.left + def is_empty(self): + """Return True if heap is empty""" + return self.min_node is None - if min_node == min_node.right: - self.min_node.key = None + def insert(self, val): + """Insert a new key into the heap""" + node = Node(val) + if not self.min_node: + self.min_node = node else: - self.min_node = min_node.right - self._consolidate() - - self.total_nodes -= 1 - 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 + 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 - Time complexity: O(log n) amortized + def delete_min(self): + """Remove and return the minimum value""" + if not self.min_node: + raise IndexError("Heap is empty") - 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 - degree_table = [Node(None)] * max_degree + min_val = self.min_node.val - roots = [] - if self.min_node: - current_root = self.min_node + # Add all children to root list + if self.min_node.child: + curr = self.min_node.child while True: - roots.append(current_root) - if current_root.right == self.min_node: + next_node = curr.right + curr.parent = None + curr.mark = False + self.min_node.add_sibling(curr) + if curr.right == self.min_node.child: break - current_root = current_root.right + curr = next_node - for current_root in roots: - root_node = current_root - current_degree = root_node.degree + # 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() - while degree_table[current_degree] is not None: - other_root = degree_table[current_degree] - if root_node.key > other_root.key: - root_node, other_root = other_root, root_node + self.size -= 1 + return min_val - other_root.left.right = other_root.right - other_root.right.left = other_root.left + def __consolidate(self): + """Consolidate trees after delete_min""" + max_degree = int(self.size**0.5) + 1 + degree_table = [None] * max_degree - if root_node.child.key is None: - root_node.child = other_root - other_root.right = other_root - other_root.left = other_root + # 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 else: - self._insert_into_circular_list(root_node.child, other_root) + self.min_node.add_sibling(degree_table[degree]) + if degree_table[degree].val < self.min_node.val: + self.min_node = degree_table[degree] - other_root.parent = root_node - root_node.degree += 1 - other_root.marked = False + 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") - degree_table[current_degree] = Node(None) - current_degree += 1 + node.val = new_val + parent = node.parent - degree_table[current_degree] = root_node + if parent and node.val < parent.val: + self.__cut(node, parent) + self.__cascading_cut(parent) - self.min_node.key = None - for degree in range(max_degree): - if degree_table[degree] is not None and ( - self.min_node is None or (degree_table[degree] < self.min_node.key) - ): - self.min_node = degree_table[degree] - - def decrease_key(self, node: Node, new_key: float | None) -> None: - """Decrease the key value of a given 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: - node: The node whose key should be decreased - new_key: The new key value, must be smaller than the current key - - Example: - >>> heap = FibonacciHeap() - >>> 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: - raise ValueError("New key is greater than current key") - - node.key = new_key - parent_node = node.parent - - if parent_node.key is not None and node.key < parent_node.key: - self._cut(node, parent_node) - self._cascading_cut(parent_node) - - if node.key < self.min_node.key: + if node.val < self.min_node.val: self.min_node = node - def _cut(self, child_node: Node, parent_node: Node) -> None: - """Cut a node from its parent and add it to the root list. - - 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: - child_node: The node to be cut from its parent - parent_node: The parent node from which to cut - - Note: - This is an internal method that maintains heap properties during - decrease_key operations. It should not be called directly from - outside the class. - """ - if child_node.right == child_node: - parent_node.child = Node(None) - else: - parent_node.child = child_node.right - child_node.right.left = child_node.left - child_node.left.right = child_node.right - - parent_node.degree -= 1 - - 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: - if not current_node.marked: - current_node.marked = True + 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 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: - float | None: The minimum key value, or None if the heap is empty - - 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 - - def is_empty(self) -> bool: - """Check if heap is empty. - - Returns: - 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 + self.__cut(node, parent) + self.__cascading_cut(parent) + + def __str__(self): + """String representation of the heap""" + if not self.min_node: + return "Empty heap" - if other_heap.min_node.key < self.min_node.key: - self.min_node = other_heap.min_node + 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 - self.total_nodes += other_heap.total_nodes + return "\n".join(print_tree(self.min_node)) if __name__ == "__main__": From a92936bd01ca5290f8a1ff78429bfd61a90c0f65 Mon Sep 17 00:00:00 2001 From: mcawezome <20085898@tafe.wa.edu.au> Date: Mon, 18 Nov 2024 12:10:01 +0800 Subject: [PATCH 07/15] Added tests and docstrings to fibonacci_heap.py --- data_structures/heap/fibonacci_heap.py | 164 ++++++++++++++++++++----- 1 file changed, 135 insertions(+), 29 deletions(-) diff --git a/data_structures/heap/fibonacci_heap.py b/data_structures/heap/fibonacci_heap.py index 28cd48bf23ad..2961da26608c 100644 --- a/data_structures/heap/fibonacci_heap.py +++ b/data_structures/heap/fibonacci_heap.py @@ -2,6 +2,7 @@ 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 @@ -11,17 +12,22 @@ - Merge: O(1) """ - class Node: """ - 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) + A node in a Fibonacci heap. + + 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): self.val = val self.parent = None @@ -32,14 +38,24 @@ def __init__(self, val): self.mark = False 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.right = self.right self.right.left = node self.right = 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 if not self.child: self.child = node @@ -48,38 +64,65 @@ def add_child(self, node): self.degree += 1 def remove(self): - """Remove this node from its sibling list""" + """Removes this node from its sibling list.""" self.left.right = self.right self.right.left = self.left 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: >>> heap = FibonacciHeap() - >>> heap.insert(3) - >>> heap.insert(2) - >>> heap.insert(15) + >>> 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): + """Initializes an empty Fibonacci heap.""" self.min_node = None self.size = 0 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 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) if not self.min_node: self.min_node = node @@ -91,13 +134,26 @@ def insert(self, val): return node 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: raise IndexError("Heap is empty") return self.min_node.val 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: return if not self.min_node: @@ -115,7 +171,13 @@ def merge_heaps(self, other): self.size += other.size 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() if node2.child: node2.child.add_sibling(node1) @@ -126,7 +188,15 @@ def __link_trees(self, node1, node2): node1.mark = False 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: raise IndexError("Heap is empty") @@ -156,8 +226,12 @@ def delete_min(self): return min_val 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 # Collect all roots @@ -195,7 +269,16 @@ def __consolidate(self): self.min_node = degree_table[degree] 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: raise ValueError("New value is greater than current value") @@ -210,7 +293,19 @@ def decrease_key(self, node, new_val): self.min_node = node 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 if parent.child == node: parent.child = node.right if node.right != node else None @@ -222,8 +317,15 @@ def __cut(self, node, parent): self.min_node.add_sibling(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: node.mark = True else: @@ -231,7 +333,12 @@ def __cascading_cut(self, node): self.__cascading_cut(parent) 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: return "Empty heap" @@ -252,5 +359,4 @@ def print_tree(node, level=0): if __name__ == "__main__": import doctest - doctest.testmod() From 071ce716e4fe5bf09fc0d89943b84e7437316767 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 18 Nov 2024 04:10:31 +0000 Subject: [PATCH 08/15] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- data_structures/heap/fibonacci_heap.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/data_structures/heap/fibonacci_heap.py b/data_structures/heap/fibonacci_heap.py index 2961da26608c..71b68ac7e8a5 100644 --- a/data_structures/heap/fibonacci_heap.py +++ b/data_structures/heap/fibonacci_heap.py @@ -12,6 +12,7 @@ - Merge: O(1) """ + class Node: """ A node in a Fibonacci heap. @@ -28,6 +29,7 @@ class Node: degree: Number of children. mark: Boolean indicating if node has lost a child. """ + def __init__(self, val): self.val = val self.parent = None @@ -231,7 +233,7 @@ def __consolidate(self): This is an internal method that maintains the heap's structure. """ - max_degree = int(self.size ** 0.5) + 1 + max_degree = int(self.size**0.5) + 1 degree_table = [None] * max_degree # Collect all roots @@ -271,11 +273,11 @@ def __consolidate(self): 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. """ @@ -299,9 +301,9 @@ def __cut(self, node, 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. """ @@ -324,8 +326,7 @@ def __cascading_cut(self, node): node: Starting node for cascading cut. """ - parent = node.parent - if parent: + if parent := node.parent: if not node.mark: node.mark = True else: @@ -359,4 +360,5 @@ def print_tree(node, level=0): if __name__ == "__main__": import doctest + doctest.testmod() From bd943b086a9a470ef853d50bbea1fa207e5cb4ec Mon Sep 17 00:00:00 2001 From: mcawezome <20085898@tafe.wa.edu.au> Date: Mon, 18 Nov 2024 12:12:17 +0800 Subject: [PATCH 09/15] Added tests and docstrings to fibonacci_heap.py --- data_structures/heap/fibonacci_heap.py | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/data_structures/heap/fibonacci_heap.py b/data_structures/heap/fibonacci_heap.py index 71b68ac7e8a5..2961da26608c 100644 --- a/data_structures/heap/fibonacci_heap.py +++ b/data_structures/heap/fibonacci_heap.py @@ -12,7 +12,6 @@ - Merge: O(1) """ - class Node: """ A node in a Fibonacci heap. @@ -29,7 +28,6 @@ class Node: degree: Number of children. mark: Boolean indicating if node has lost a child. """ - def __init__(self, val): self.val = val self.parent = None @@ -233,7 +231,7 @@ def __consolidate(self): This is an internal method that maintains the heap's structure. """ - max_degree = int(self.size**0.5) + 1 + max_degree = int(self.size ** 0.5) + 1 degree_table = [None] * max_degree # Collect all roots @@ -273,11 +271,11 @@ def __consolidate(self): 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. """ @@ -301,9 +299,9 @@ def __cut(self, node, 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. """ @@ -326,7 +324,8 @@ def __cascading_cut(self, node): node: Starting node for cascading cut. """ - if parent := node.parent: + parent = node.parent + if parent: if not node.mark: node.mark = True else: @@ -360,5 +359,4 @@ def print_tree(node, level=0): if __name__ == "__main__": import doctest - doctest.testmod() From a1291fd5cacbba87cd8e5a9b38cce5e919334cc2 Mon Sep 17 00:00:00 2001 From: mcawezome <20085898@tafe.wa.edu.au> Date: Mon, 18 Nov 2024 12:21:36 +0800 Subject: [PATCH 10/15] Added tests and docstrings to fibonacci_heap.py --- data_structures/heap/fibonacci_heap.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data_structures/heap/fibonacci_heap.py b/data_structures/heap/fibonacci_heap.py index 2961da26608c..13e58a0d8305 100644 --- a/data_structures/heap/fibonacci_heap.py +++ b/data_structures/heap/fibonacci_heap.py @@ -294,7 +294,7 @@ def decrease_key(self, node, new_val): def __cut(self, node, parent): """ - Cuts a node from its parent. + Cuts a node from its parent Args: node: Node to be cut. From c4516d1dfc757210808c23c545896595ca561861 Mon Sep 17 00:00:00 2001 From: mcawezome <20085898@tafe.wa.edu.au> Date: Mon, 18 Nov 2024 12:26:21 +0800 Subject: [PATCH 11/15] Added tests and docstrings to fibonacci_heap.py --- data_structures/heap/fibonacci_heap.py | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/data_structures/heap/fibonacci_heap.py b/data_structures/heap/fibonacci_heap.py index 13e58a0d8305..79ed58ec5edd 100644 --- a/data_structures/heap/fibonacci_heap.py +++ b/data_structures/heap/fibonacci_heap.py @@ -271,11 +271,11 @@ def __consolidate(self): 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. """ @@ -299,11 +299,6 @@ def __cut(self, node, 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 From 144030aed1079d9a8c1bb06db36aac3459ac15f1 Mon Sep 17 00:00:00 2001 From: mcawezome <20085898@tafe.wa.edu.au> Date: Thu, 21 Nov 2024 22:10:25 +1100 Subject: [PATCH 12/15] Retried commit with base fib heap implementation2 --- data_structures/heap/fibonacci_heap.py | 455 ++++++++----------------- 1 file changed, 138 insertions(+), 317 deletions(-) diff --git a/data_structures/heap/fibonacci_heap.py b/data_structures/heap/fibonacci_heap.py index 79ed58ec5edd..68ad764bee0d 100644 --- a/data_structures/heap/fibonacci_heap.py +++ b/data_structures/heap/fibonacci_heap.py @@ -1,357 +1,178 @@ -""" -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 +import math -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: - """ - A node in a Fibonacci heap. - - 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): - self.val = val +class FibonacciHeapNode: + def __init__(self, key, value=None): + self.key = key + self.value = value + self.degree = 0 self.parent = None self.child = None - self.left = 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 + self.next = self + self.prev = self 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) + node.prev = self.child + node.next = self.child.next + self.child.next.prev = node + self.child.next = node + node.parent = self self.degree += 1 - def remove(self): - """Removes this node from its sibling list.""" - self.left.right = self.right - self.right.left = self.left + def remove_child(self, node): + if node.next == node: # Single child + self.child = None + 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: - """ - 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): - """Initializes an empty Fibonacci heap.""" self.min_node = None - self.size = 0 + self.total_nodes = 0 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 - def insert(self, val): - """ - 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: + def insert(self, key, value=None): + node = FibonacciHeapNode(key, value) + self._merge_with_root_list(node) + if not self.min_node or node.key < self.min_node.key: self.min_node = node - else: - self.min_node.add_sibling(node) - if node.val < self.min_node.val: - self.min_node = node - self.size += 1 + self.total_nodes += 1 return node - def peek(self): - """ - 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: - raise IndexError("Heap is empty") - return self.min_node.val - - def merge_heaps(self, other): - """ - Merges another Fibonacci heap into this one. + def find_min(self): + return self.min_node - Args: - other: Another FibonacciHeap instance to merge with this one. - """ - if not other.min_node: - return + def union(self, other_heap): + if not other_heap.min_node: + return self 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) + self.min_node = other_heap.min_node 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. + self._merge_with_root_list(other_heap.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 + + 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) - Raises: - IndexError: If the heap is empty. - """ + def _merge_with_root_list(self, node): 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 + 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 - # Remove minimum node - if self.min_node.right == self.min_node: + def _remove_from_root_list(self, node): + if node.next == 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 = [] - 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 + node.prev.next = node.next + node.next.prev = node.prev + + def _consolidate(self): + 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 degree in range(max_degree): - if degree_table[degree]: + for i in range(array_size): + if array[i]: 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 + self.min_node = array[i] 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. + self._merge_with_root_list(array[i]) + if array[i].key < self.min_node.key: + self.min_node = array[i] - Args: - node: The node whose value should be decreased. - new_val: The new value for the node. + def _link(self, y, x): + self._remove_from_root_list(y) + x.add_child(y) + y.mark = False - 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)) + def _iterate(self, start): + node = start + while True: + yield node + node = node.next + if node == start: + break +# Example usage if __name__ == "__main__": - import doctest - doctest.testmod() + 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 From 502953aa70bc4fbda148c666ffb3a3855e506221 Mon Sep 17 00:00:00 2001 From: mcawezome <20085898@tafe.wa.edu.au> Date: Mon, 25 Nov 2024 11:57:05 +1100 Subject: [PATCH 13/15] Added full implementation of fibonacci heap --- data_structures/heap/fibonacci_heap.py | 339 ++++++++++++++++++++----- 1 file changed, 271 insertions(+), 68 deletions(-) diff --git a/data_structures/heap/fibonacci_heap.py b/data_structures/heap/fibonacci_heap.py index 68ad764bee0d..b1a182b3543d 100644 --- a/data_structures/heap/fibonacci_heap.py +++ b/data_structures/heap/fibonacci_heap.py @@ -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 @@ def __init__(self, key, value=None): 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 @@ def add_child(self, node): 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 @@ def remove_child(self, node): 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,65 +152,129 @@ def insert(self, key, value=None): return node def find_min(self): - return self.min_node + """ + Finds the node with the minimum key. - def union(self, other_heap): - if not other_heap.min_node: - return self - if not self.min_node: - self.min_node = other_heap.min_node - else: - self._merge_with_root_list(other_heap.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 + 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): - z = self.min_node - if z: - if z.child: - children = list(self._iterate(z.child)) + """ + 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(z) - if z == z.next: + self._remove_from_root_list(temp_min_node) + if temp_min_node == temp_min_node.next: self.min_node = None else: - self.min_node = z.next + self.min_node = temp_min_node.next self._consolidate() self.total_nodes -= 1 - return z + return temp_min_node - def decrease_key(self, x, new_key): - if new_key > x.key: + 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") - 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 + 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 _cut(self, x, y): - y.remove_child(x) - self._merge_with_root_list(x) - x.mark = False + def union(self, other_heap): + """ + Merges another Fibonacci Heap into this heap. - 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) + 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 + if not self.min_node: + self.min_node = other_heap.min_node + else: + self._merge_with_root_list(other_heap.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 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 @@ def _merge_with_root_list(self, node): 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 @@ def _remove_from_root_list(self, node): 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 @@ def _consolidate(self): 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 From 7b987a0dd92e085ad8ae130ea94725118b83e72e Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 25 Nov 2024 02:01:03 +0000 Subject: [PATCH 14/15] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- data_structures/heap/fibonacci_heap.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/data_structures/heap/fibonacci_heap.py b/data_structures/heap/fibonacci_heap.py index b1a182b3543d..d68fdde6b4d5 100644 --- a/data_structures/heap/fibonacci_heap.py +++ b/data_structures/heap/fibonacci_heap.py @@ -355,8 +355,7 @@ def _cascading_cut(self, node_to_cut): Args: node_to_cut (FibonacciHeapNode): The node to be cut recursively. """ - temp_parent = node_to_cut.parent - if temp_parent: + if temp_parent := node_to_cut.parent: if not node_to_cut.mark: node_to_cut.mark = True else: From 660976f1c731b190d8f2c1b3d95eb5444dabdc84 Mon Sep 17 00:00:00 2001 From: mcawezome <20085898@tafe.wa.edu.au> Date: Mon, 25 Nov 2024 13:10:34 +1100 Subject: [PATCH 15/15] Added complete implementation of Fibonacci Heap --- data_structures/heap/fibonacci_heap.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/data_structures/heap/fibonacci_heap.py b/data_structures/heap/fibonacci_heap.py index d68fdde6b4d5..4caaa00fd59e 100644 --- a/data_structures/heap/fibonacci_heap.py +++ b/data_structures/heap/fibonacci_heap.py @@ -38,9 +38,11 @@ class FibonacciHeapNode: 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. + 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. + prev (FibonacciHeapNode): The previous sibling + in the circular doubly-linked list. """ def __init__(self, key, value=None):