-
-
Notifications
You must be signed in to change notification settings - Fork 48.6k
Added an implementation of fibonacci heap. #12375
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 6 commits
a71d17a
a21a624
2e35261
fe95a31
369994d
ea5a187
a92936b
071ce71
bd943b0
a1291fd
c4516d1
144030a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,256 @@ | ||
""" | ||
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: | ||
""" | ||
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, 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): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As there is no test file in this pull request nor any test function or class in the file Please provide return type hint for the function: Please provide type hint for the parameter: |
||
"""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: | ||
""" | ||
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): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please provide return type hint for the function: There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please provide return type hint for the function: |
||
self.min_node = None | ||
self.size = 0 | ||
|
||
def is_empty(self): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please provide return type hint for the function: |
||
"""Return True if heap is empty""" | ||
return self.min_node is 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.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 | ||
|
||
def delete_min(self): | ||
|
||
"""Remove and return the minimum value""" | ||
if not self.min_node: | ||
raise IndexError("Heap is empty") | ||
|
||
min_val = self.min_node.val | ||
|
||
# Add all children to root list | ||
if self.min_node.child: | ||
curr = self.min_node.child | ||
while True: | ||
next_node = curr.right | ||
curr.parent = None | ||
curr.mark = False | ||
self.min_node.add_sibling(curr) | ||
if curr.right == self.min_node.child: | ||
break | ||
curr = next_node | ||
|
||
# Remove minimum node | ||
if self.min_node.right == self.min_node: | ||
self.min_node = None | ||
else: | ||
self.min_node.remove() | ||
self.min_node = self.min_node.right | ||
self.__consolidate() | ||
|
||
self.size -= 1 | ||
return min_val | ||
|
||
def __consolidate(self): | ||
|
||
"""Consolidate trees after delete_min""" | ||
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 | ||
self.min_node = None | ||
for degree in range(max_degree): | ||
if degree_table[degree]: | ||
if not self.min_node: | ||
self.min_node = degree_table[degree] | ||
self.min_node.left = self.min_node | ||
self.min_node.right = self.min_node | ||
else: | ||
self.min_node.add_sibling(degree_table[degree]) | ||
if degree_table[degree].val < self.min_node.val: | ||
self.min_node = degree_table[degree] | ||
|
||
def decrease_key(self, node, new_val): | ||
|
||
"""Decrease the value of a node""" | ||
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): | ||
|
||
"""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(node, parent) | ||
self.__cascading_cut(parent) | ||
|
||
def __str__(self): | ||
|
||
"""String representation of the heap""" | ||
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)) | ||
|
||
|
||
if __name__ == "__main__": | ||
import doctest | ||
|
||
doctest.testmod() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please provide return type hint for the function:
__init__
. If the function does not return a value, please provide the type hint as:def function() -> None:
Please provide type hint for the parameter:
val