Skip to content
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

Fibonacci Heap Implementation #12397

Open
wants to merge 15 commits into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
382 changes: 382 additions & 0 deletions data_structures/heap/fibonacci_heap.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,382 @@
"""
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):

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: key

Please provide type hint for the parameter: value

"""
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
self.parent = None
self.child = None
self.mark = False
self.next = self
self.prev = self

def add_child(self, node):

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: add_child. If the function does not return a value, please provide the type hint as: def function() -> None:

Please provide type hint for the parameter: 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:
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_child(self, node):

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: remove_child. If the function does not return a value, please provide the type hint as: def function() -> None:

Please provide type hint for the parameter: 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:
self.child = node.next
node.prev.next = node.next
node.next.prev = node.prev
node.parent = None
self.degree -= 1


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):

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:

"""
Initializes an empty Fibonacci Heap.
"""
self.min_node = None
self.total_nodes = 0

def is_empty(self):

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: is_empty. If the function does not return a value, please provide the type hint as: def function() -> None:

"""
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):

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: insert. If the function does not return a value, please provide the type hint as: def function() -> None:

Please provide type hint for the parameter: key

Please provide type hint for the parameter: value

"""
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:
self.min_node = node
self.total_nodes += 1
return node

def find_min(self):

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: find_min. If the function does not return a value, please provide the type hint as: def function() -> None:

"""
Finds the node with the minimum key.

Returns:
FibonacciHeapNode: The node with the minimum key.

Examples:
>>> fh = FibonacciHeap()
>>> n1 = fh.insert(10)
>>> n2 = fh.insert(2)
>>> fh.find_min().key
2
"""
return self.min_node

def extract_min(self):

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: extract_min. If the function does not return a value, please provide the type hint as: def function() -> None:

"""
Removes and returns the node with the minimum key.

Returns:
FibonacciHeapNode: The node with the minimum key.

Examples:
>>> fh = FibonacciHeap()
>>> n1 = fh.insert(10)
>>> n2 = fh.insert(2)
>>> fh.extract_min().key
2
"""
temp_min_node = self.min_node
if temp_min_node:
if temp_min_node.child:
children = list(self._iterate(temp_min_node.child))
for child in children:
self._merge_with_root_list(child)
child.parent = None
self._remove_from_root_list(temp_min_node)
if temp_min_node == temp_min_node.next:
self.min_node = None
else:
self.min_node = temp_min_node.next
self._consolidate()
self.total_nodes -= 1
return temp_min_node

def decrease_key(self, node, new_key):

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: decrease_key. If the function does not return a value, please provide the type hint as: def function() -> None:

Please provide type hint for the parameter: node

Please provide type hint for the parameter: new_key

"""
Decreases the key of a given node.

Args:
node (FibonacciHeapNode): The node to decrease the key for.
new_key (any): The new key value.

Raises:
ValueError: If the new key is greater than the current key.

Examples:
>>> fh = FibonacciHeap()
>>> node = fh.insert(10)
>>> fh.decrease_key(node, 5)
>>> fh.find_min().key
5
"""
if new_key > node.key:
raise ValueError("New key is greater than current key")
node.key = new_key
temp_parent = node.parent
if temp_parent and node.key < temp_parent.key:
self._cut(node, temp_parent)
self._cascading_cut(temp_parent)
if node.key < self.min_node.key:
self.min_node = node

def delete(self, x):

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: delete. If the function does not return a value, please provide the type hint as: def function() -> None:

Please provide type hint for the parameter: x

Please provide descriptive name for the parameter: x

"""
Deletes a given node from the heap.

Args:
x (FibonacciHeapNode): The node to be deleted.

Examples:
>>> fh = FibonacciHeap()
>>> node = fh.insert(10)
>>> fh.delete(node)
>>> fh.is_empty()
True
"""
self.decrease_key(x, -math.inf)
self.extract_min()

def union(self, other_heap):

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: union. If the function does not return a value, please provide the type hint as: def function() -> None:

Please provide type hint for the parameter: other_heap

"""
Merges another Fibonacci Heap into this heap.

Args:
other_heap (FibonacciHeap): The other Fibonacci Heap to be merged.

Examples:
>>> fh1 = FibonacciHeap()
>>> fh2 = FibonacciHeap()
>>> n1 = fh1.insert(10)
>>> n2 = fh2.insert(5)
>>> fh1.union(fh2)
>>> fh1.find_min().key
5
"""
if not other_heap.min_node:
return
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):

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: _merge_with_root_list. If the function does not return a value, please provide the type hint as: def function() -> None:

Please provide type hint for the parameter: 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:
node.prev = self.min_node
node.next = self.min_node.next
self.min_node.next.prev = node
self.min_node.next = node

def _remove_from_root_list(self, node):

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: _remove_from_root_list. If the function does not return a value, please provide the type hint as: def function() -> None:

Please provide type hint for the parameter: 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:
node.prev.next = node.next
node.next.prev = node.prev

def _consolidate(self):

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: _consolidate. If the function does not return a value, please provide the type hint as: def function() -> None:

"""
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 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]:
if not self.min_node:
self.min_node = array[i]
else:
self._merge_with_root_list(array[i])
if array[i].key < self.min_node.key:
self.min_node = array[i]

def _link(self, node_to_link, node_to_parent):

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: _link. If the function does not return a value, please provide the type hint as: def function() -> None:

Please provide type hint for the parameter: node_to_link

Please provide type hint for the parameter: 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):

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: _cut. If the function does not return a value, please provide the type hint as: def function() -> None:

Please provide type hint for the parameter: node_to_cut

Please provide type hint for the parameter: 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):

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: _cascading_cut. If the function does not return a value, please provide the type hint as: def function() -> None:

Please provide type hint for the parameter: node_to_cut

"""
Performs a cascading cut operation.

Args:
node_to_cut (FibonacciHeapNode): The node to be cut recursively.
"""
if temp_parent := node_to_cut.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):

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: _iterate. If the function does not return a value, please provide the type hint as: def function() -> None:

Please provide type hint for the parameter: 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
Loading