From 6c75af8df0c5d9fda142b42494ba2ccb0bb9d352 Mon Sep 17 00:00:00 2001 From: Sayfulla Mirkhalikov Date: Wed, 27 Sep 2023 11:34:04 +0500 Subject: [PATCH 1/8] Added fibonacci heap data structure --- data_structures/heap/fibonacci_heap.py | 211 +++++++++++++++++++++++++ 1 file changed, 211 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..5f636b48158c --- /dev/null +++ b/data_structures/heap/fibonacci_heap.py @@ -0,0 +1,211 @@ +class Node: + """ + The Node class represents a node in a Fibonacci heap. + """ + + def __init__(self, key): + self.key = key + self.degree = 0 + self.parent = None + self.child = None + self.left = self + self.right = self + self.mark = False + + +class FibonacciHeap: + """ + FibonacciHeap is a implementation of Fibonacci heap. + """ + + def __init__(self): + self.min = None + self.num_nodes = 0 + + def insert(self, key): + """ + Inserts a new node to the heap. + """ + node = Node(key) + if self.min is None: + self.min = node + else: + self._insert_node(node) + if node.key < self.min.key: + self.min = node + self.num_nodes += 1 + + def _insert_node(self, node): + node.left = self.min + node.right = self.min.right + self.min.right = node + node.right.left = node + + def get_min(self): + """Return min node's key.""" + return self.min.key + + def extract_min(self): + """Extract (delete) the min node from the heap.""" + if self.min is None: + return None + min_node = self.min + if min_node.child is not None: + child = min_node.child + while True: + next_node = child.right + self._insert_node(child) + child.parent = None + child = next_node + if child == min_node.child: + break + min_node.left.right = min_node.right + min_node.right.left = min_node.left + if min_node == min_node.right: + self.min = None + else: + self.min = min_node.right + self._consolidate() + self.num_nodes -= 1 + return min_node.key + + def union(self, other_heap): + """Union (merge) two fibonacci heaps.""" + new_heap = FibonacciHeap() + + new_heap.min = self.min + if self.min is not None and other_heap.min is not None: + self.min.right.left = other_heap.min.left + other_heap.min.left.right = self.min.right + self.min.right = other_heap.min + other_heap.min.left = self.min + if other_heap.min.key < self.min.key: + self.min = other_heap.min + elif other_heap.min is not None: + self.min = other_heap.min + + new_heap.num_nodes = self.num_nodes + other_heap.num_nodes + + self.min = None + self.num_nodes = 0 + other_heap.min = None + other_heap.num_nodes = 0 + + return new_heap + + def _consolidate(self): + A = [None] * self.num_nodes + nodes = [x for x in self._get_nodes()] + for node in nodes: + degree = node.degree + while A[degree] is not None: + other = A[degree] + if node.key > other.key: + node, other = other, node + self._link(other, node) + A[degree] = None + degree += 1 + A[degree] = node + self.min = None + for node in A: + if node is not None: + if self.min is None: + self.min = node + else: + self._insert_node(node) + if node.key < self.min.key: + self.min = node + + def _link(self, y, x): + y.left.right = y.right + y.right.left = y.left + y.parent = x + if x.child is None: + x.child = y + y.right = y + y.left = y + else: + y.left = x.child + y.right = x.child.right + x.child.right = y + y.right.left = y + x.degree += 1 + + def decrease_key(self, node, new_key): + """Modify the key of some node in the heap.""" + if new_key > node.key: + return + node.key = new_key + parent = node.parent + if parent is not None and node.key < parent.key: + self._cut(node, parent) + self._cascading_cut(parent) + if node.key < self.min.key: + self.min = node + + def _cut(self, x, y): + x.left.right = x.right + x.right.left = x.left + y.degree -= 1 + if x == x.right: + y.child = None + elif y.child == x: + y.child = x.right + x.parent = None + x.mark = False + self._insert_node(x) + + def _cascading_cut(self, y): + z = y.parent + if z is not None: + if not y.mark: + y.mark = True + else: + self._cut(y, z) + self._cascading_cut(z) + + def _get_nodes(self): + nodes = [] + if self.min is not None: + node = self.min + while True: + nodes.append(node) + node = node.right + if node == self.min: + break + return nodes + + +def test_fibonacci_heap() -> None: + """ + >>> h = FibonacciHeap() + >>> h.insert(10) + >>> h.insert(5) + >>> h.insert(7) + >>> h.get_min() + 5 + >>> h.insert(3) + >>> h.extract_min() + 3 + >>> h.get_min() + 5 + >>> h.decrease_key(h.min, 2) + >>> h.get_min() + 2 + >>> h2 = FibonacciHeap() + >>> h2.insert(8) + >>> h2.insert(6) + >>> h2.insert(4) + >>> h2.get_min() + 4 + >>> h3 = h.union(h2) + >>> h3.get_min() + 2 + """ + + +if __name__ == "__main__": + import doctest + + # run doc test + doctest.testmod() From 949318ebb762490a5627186bde2e94a4cfd59b74 Mon Sep 17 00:00:00 2001 From: Sayfulla Mirkhalikov Date: Wed, 27 Sep 2023 11:56:44 +0500 Subject: [PATCH 2/8] Added fibonacci heap with type hints and doctest. --- data_structures/heap/fibonacci_heap.py | 40 +++++++++++++------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/data_structures/heap/fibonacci_heap.py b/data_structures/heap/fibonacci_heap.py index 5f636b48158c..ac794c39d43a 100644 --- a/data_structures/heap/fibonacci_heap.py +++ b/data_structures/heap/fibonacci_heap.py @@ -3,7 +3,7 @@ class Node: The Node class represents a node in a Fibonacci heap. """ - def __init__(self, key): + def __init__(self, key: int) -> None: self.key = key self.degree = 0 self.parent = None @@ -18,11 +18,11 @@ class FibonacciHeap: FibonacciHeap is a implementation of Fibonacci heap. """ - def __init__(self): + def __init__(self) -> None: self.min = None self.num_nodes = 0 - def insert(self, key): + def insert(self, key: int) -> None: """ Inserts a new node to the heap. """ @@ -35,17 +35,17 @@ def insert(self, key): self.min = node self.num_nodes += 1 - def _insert_node(self, node): + def _insert_node(self, node: Node) -> None: node.left = self.min node.right = self.min.right self.min.right = node node.right.left = node - def get_min(self): + def get_min(self) -> int: """Return min node's key.""" return self.min.key - def extract_min(self): + def extract_min(self) -> int: """Extract (delete) the min node from the heap.""" if self.min is None: return None @@ -69,7 +69,7 @@ def extract_min(self): self.num_nodes -= 1 return min_node.key - def union(self, other_heap): + def union(self, other_heap: "FibonacciHeap") -> "FibonacciHeap": """Union (merge) two fibonacci heaps.""" new_heap = FibonacciHeap() @@ -93,21 +93,21 @@ def union(self, other_heap): return new_heap - def _consolidate(self): - A = [None] * self.num_nodes - nodes = [x for x in self._get_nodes()] + def _consolidate(self) -> None: + aux = [None] * self.num_nodes + nodes = self._get_nodes() for node in nodes: degree = node.degree - while A[degree] is not None: - other = A[degree] + while aux[degree] is not None: + other = aux[degree] if node.key > other.key: node, other = other, node self._link(other, node) - A[degree] = None + aux[degree] = None degree += 1 - A[degree] = node + aux[degree] = node self.min = None - for node in A: + for node in aux: if node is not None: if self.min is None: self.min = node @@ -116,7 +116,7 @@ def _consolidate(self): if node.key < self.min.key: self.min = node - def _link(self, y, x): + def _link(self, y: Node, x: Node) -> None: y.left.right = y.right y.right.left = y.left y.parent = x @@ -131,7 +131,7 @@ def _link(self, y, x): y.right.left = y x.degree += 1 - def decrease_key(self, node, new_key): + def decrease_key(self, node: Node, new_key: int) -> None: """Modify the key of some node in the heap.""" if new_key > node.key: return @@ -143,7 +143,7 @@ def decrease_key(self, node, new_key): if node.key < self.min.key: self.min = node - def _cut(self, x, y): + def _cut(self, x: Node, y: Node) -> None: x.left.right = x.right x.right.left = x.left y.degree -= 1 @@ -155,7 +155,7 @@ def _cut(self, x, y): x.mark = False self._insert_node(x) - def _cascading_cut(self, y): + def _cascading_cut(self, y: Node) -> None: z = y.parent if z is not None: if not y.mark: @@ -164,7 +164,7 @@ def _cascading_cut(self, y): self._cut(y, z) self._cascading_cut(z) - def _get_nodes(self): + def _get_nodes(self) -> list: nodes = [] if self.min is not None: node = self.min From 560237487da06e09ad56fd51ee1321feef58463e Mon Sep 17 00:00:00 2001 From: Sayfulla Mirkhalikov Date: Wed, 27 Sep 2023 12:01:44 +0500 Subject: [PATCH 3/8] Added wikipedia page url. --- data_structures/heap/fibonacci_heap.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/data_structures/heap/fibonacci_heap.py b/data_structures/heap/fibonacci_heap.py index ac794c39d43a..54c16b0cbdb0 100644 --- a/data_structures/heap/fibonacci_heap.py +++ b/data_structures/heap/fibonacci_heap.py @@ -1,3 +1,7 @@ +""" +All about Fibonacci heap: https://en.wikipedia.org/wiki/Fibonacci_heap +""" + class Node: """ The Node class represents a node in a Fibonacci heap. From 0f01f8030bfa5ae77bb505a49160a8fe255bd58e Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 27 Sep 2023 07:07:06 +0000 Subject: [PATCH 4/8] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- data_structures/heap/fibonacci_heap.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/data_structures/heap/fibonacci_heap.py b/data_structures/heap/fibonacci_heap.py index 54c16b0cbdb0..89fb5336af2a 100644 --- a/data_structures/heap/fibonacci_heap.py +++ b/data_structures/heap/fibonacci_heap.py @@ -2,6 +2,7 @@ All about Fibonacci heap: https://en.wikipedia.org/wiki/Fibonacci_heap """ + class Node: """ The Node class represents a node in a Fibonacci heap. @@ -160,8 +161,7 @@ def _cut(self, x: Node, y: Node) -> None: self._insert_node(x) def _cascading_cut(self, y: Node) -> None: - z = y.parent - if z is not None: + if (z := y.parent) is not None: if not y.mark: y.mark = True else: From 7ac0eb95496a8c0a91d06dcac3135c3f663f4720 Mon Sep 17 00:00:00 2001 From: Sayfulla Mirkhalikov Date: Wed, 27 Sep 2023 13:10:37 +0500 Subject: [PATCH 5/8] Fixed mypy error --- data_structures/heap/fibonacci_heap.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/data_structures/heap/fibonacci_heap.py b/data_structures/heap/fibonacci_heap.py index 89fb5336af2a..ad9d53488054 100644 --- a/data_structures/heap/fibonacci_heap.py +++ b/data_structures/heap/fibonacci_heap.py @@ -1,6 +1,7 @@ """ All about Fibonacci heap: https://en.wikipedia.org/wiki/Fibonacci_heap """ +from typing import List, Union, Any class Node: @@ -11,8 +12,8 @@ class Node: def __init__(self, key: int) -> None: self.key = key self.degree = 0 - self.parent = None - self.child = None + self.parent: Any = None + self.child: Any = None self.left = self self.right = self self.mark = False @@ -24,8 +25,8 @@ class FibonacciHeap: """ def __init__(self) -> None: - self.min = None - self.num_nodes = 0 + self.min: Union[Node, Any] = None + self.num_nodes: int = 0 def insert(self, key: int) -> None: """ @@ -50,7 +51,7 @@ def get_min(self) -> int: """Return min node's key.""" return self.min.key - def extract_min(self) -> int: + def extract_min(self) -> int | None: """Extract (delete) the min node from the heap.""" if self.min is None: return None @@ -99,7 +100,7 @@ def union(self, other_heap: "FibonacciHeap") -> "FibonacciHeap": return new_heap def _consolidate(self) -> None: - aux = [None] * self.num_nodes + aux: List[Union[Node, Any]] = [None] * self.num_nodes nodes = self._get_nodes() for node in nodes: degree = node.degree @@ -168,8 +169,8 @@ def _cascading_cut(self, y: Node) -> None: self._cut(y, z) self._cascading_cut(z) - def _get_nodes(self) -> list: - nodes = [] + def _get_nodes(self) -> List[Node]: + nodes: List[Node] = [] if self.min is not None: node = self.min while True: From 89ad5f8e07b63799fe0ea9c9588ebd56a03e26e0 Mon Sep 17 00:00:00 2001 From: Sayfulla Mirkhalikov Date: Wed, 27 Sep 2023 13:15:17 +0500 Subject: [PATCH 6/8] Fixed ruff error. --- data_structures/heap/fibonacci_heap.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/data_structures/heap/fibonacci_heap.py b/data_structures/heap/fibonacci_heap.py index ad9d53488054..fa29656785a9 100644 --- a/data_structures/heap/fibonacci_heap.py +++ b/data_structures/heap/fibonacci_heap.py @@ -1,7 +1,7 @@ """ All about Fibonacci heap: https://en.wikipedia.org/wiki/Fibonacci_heap """ -from typing import List, Union, Any +from typing import Union, Any class Node: @@ -100,7 +100,7 @@ def union(self, other_heap: "FibonacciHeap") -> "FibonacciHeap": return new_heap def _consolidate(self) -> None: - aux: List[Union[Node, Any]] = [None] * self.num_nodes + aux: list[Union[Node, Any]] = [None] * self.num_nodes nodes = self._get_nodes() for node in nodes: degree = node.degree @@ -169,8 +169,8 @@ def _cascading_cut(self, y: Node) -> None: self._cut(y, z) self._cascading_cut(z) - def _get_nodes(self) -> List[Node]: - nodes: List[Node] = [] + def _get_nodes(self) -> list[Node]: + nodes: list[Node] = [] if self.min is not None: node = self.min while True: From 887e79d2ab022f6fd4b58309b395c3ca0912be52 Mon Sep 17 00:00:00 2001 From: Sayfulla Mirkhalikov Date: Wed, 27 Sep 2023 13:26:22 +0500 Subject: [PATCH 7/8] Provided a descriptive name for the parameter --- data_structures/heap/fibonacci_heap.py | 62 +++++++++++++------------- 1 file changed, 31 insertions(+), 31 deletions(-) diff --git a/data_structures/heap/fibonacci_heap.py b/data_structures/heap/fibonacci_heap.py index fa29656785a9..64b84261b1d2 100644 --- a/data_structures/heap/fibonacci_heap.py +++ b/data_structures/heap/fibonacci_heap.py @@ -122,20 +122,20 @@ def _consolidate(self) -> None: if node.key < self.min.key: self.min = node - def _link(self, y: Node, x: Node) -> None: - y.left.right = y.right - y.right.left = y.left - y.parent = x - if x.child is None: - x.child = y - y.right = y - y.left = y + def _link(self, other: Node, node: Node) -> None: + other.left.right = other.right + other.right.left = other.left + other.parent = node + if node.child is None: + node.child = other + other.right = other + other.left = other else: - y.left = x.child - y.right = x.child.right - x.child.right = y - y.right.left = y - x.degree += 1 + other.left = node.child + other.right = node.child.right + node.child.right = other + other.right.left = other + node.degree += 1 def decrease_key(self, node: Node, new_key: int) -> None: """Modify the key of some node in the heap.""" @@ -149,25 +149,25 @@ def decrease_key(self, node: Node, new_key: int) -> None: if node.key < self.min.key: self.min = node - def _cut(self, x: Node, y: Node) -> None: - x.left.right = x.right - x.right.left = x.left - y.degree -= 1 - if x == x.right: - y.child = None - elif y.child == x: - y.child = x.right - x.parent = None - x.mark = False - self._insert_node(x) - - def _cascading_cut(self, y: Node) -> None: - if (z := y.parent) is not None: - if not y.mark: - y.mark = True + def _cut(self, current_node: Node, parent_node: Node) -> None: + current_node.left.right = current_node.right + current_node.right.left = current_node.left + parent_node.degree -= 1 + if current_node == current_node.right: + parent_node.child = None + elif parent_node.child == current_node: + parent_node.child = current_node.right + current_node.parent = None + current_node.mark = False + self._insert_node(current_node) + + def _cascading_cut(self, node: Node) -> None: + if (parent := node.parent) is not None: + if not node.mark: + node.mark = True else: - self._cut(y, z) - self._cascading_cut(z) + self._cut(node, parent) + self._cascading_cut(parent) def _get_nodes(self) -> list[Node]: nodes: list[Node] = [] From c13595272eed92d57cfdcea0a9bc4cacb93075e9 Mon Sep 17 00:00:00 2001 From: Mirkhalikov Sayfulla Date: Wed, 27 Sep 2023 13:41:18 +0500 Subject: [PATCH 8/8] Updated fibonacci_heap.py Fixed ruff error and pre-commit error --- 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 64b84261b1d2..5a87576a997f 100644 --- a/data_structures/heap/fibonacci_heap.py +++ b/data_structures/heap/fibonacci_heap.py @@ -1,7 +1,7 @@ """ All about Fibonacci heap: https://en.wikipedia.org/wiki/Fibonacci_heap """ -from typing import Union, Any +from typing import Any class Node: @@ -25,7 +25,7 @@ class FibonacciHeap: """ def __init__(self) -> None: - self.min: Union[Node, Any] = None + self.min: Node | Any = None self.num_nodes: int = 0 def insert(self, key: int) -> None: @@ -100,7 +100,7 @@ def union(self, other_heap: "FibonacciHeap") -> "FibonacciHeap": return new_heap def _consolidate(self) -> None: - aux: list[Union[Node, Any]] = [None] * self.num_nodes + aux: list[Node | Any] = [None] * self.num_nodes nodes = self._get_nodes() for node in nodes: degree = node.degree