Skip to content

Commit

Permalink
[WIP] Added Binomial Heap (#68)
Browse files Browse the repository at this point in the history
* added BinomialTreeNode

* added BinomialTree

* added BinomialHeap
  • Loading branch information
czgdp1807 authored Dec 30, 2019
1 parent d754853 commit b2e3900
Show file tree
Hide file tree
Showing 8 changed files with 485 additions and 15 deletions.
6 changes: 6 additions & 0 deletions pydatastructs/miscellaneous_data_structures/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,14 @@

from . import (
stack,
binomial_trees
)

from .binomial_trees import (
BinomialTree
)
__all__.extend(binomial_trees.__all__)

from .stack import (
Stack,
)
Expand Down
79 changes: 79 additions & 0 deletions pydatastructs/miscellaneous_data_structures/binomial_trees.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
from pydatastructs.utils.misc_util import BinomialTreeNode, _check_type

__all__ = [
'BinomialTree'
]

class BinomialTree(object):
"""
Represents binomial trees
Parameters
==========
root: BinomialTreeNode
The root of the binomial tree.
By default, None
order: int
The order of the binomial tree.
By default, None
Examples
========
>>> from pydatastructs import BinomialTree, BinomialTreeNode
>>> root = BinomialTreeNode(1, 1)
>>> tree = BinomialTree(root, 0)
>>> tree.is_empty
False
References
==========
.. [1] https://en.wikipedia.org/wiki/Binomial_heap
"""
__slots__ = ['root', 'order']

def __new__(cls, root=None, order=None):
if root is not None and \
not _check_type(root, BinomialTreeNode):
raise TypeError("%s i.e., root should be of "
"type BinomialTreeNode."%(root))
if order is not None and not _check_type(order, int):
raise TypeError("%s i.e., order should be of "
"type int."%(order))
obj = object.__new__(cls)
if root is not None:
root.is_root = True
obj.root = root
obj.order = order
return obj

def add_sub_tree(self, other_tree):
"""
Adds a sub tree to current tree.
Parameters
==========
other_tree: BinomialTree
Raises
======
ValueError: If order of the two trees
are different.
"""
if not _check_type(other_tree, BinomialTree):
raise TypeError("%s i.e., other_tree should be of "
"type BinomialTree"%(other_tree))
if self.order != other_tree.order:
raise ValueError("Orders of both the trees should be same.")
self.root.children.append(other_tree.root)
other_tree.root.parent = self.root
other_tree.root.is_root = False
self.order += 1

@property
def is_empty(self):
return self.root is None
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
from pydatastructs.miscellaneous_data_structures.binomial_trees import BinomialTree
from pydatastructs.utils.raises_util import raises
from pydatastructs.utils.misc_util import BinomialTreeNode

# only tests the corner cases
def test_BinomialTree():
assert raises(TypeError, lambda: BinomialTree(1, 1))
assert raises(TypeError, lambda: BinomialTree(None, 1.5))

bt = BinomialTree()
assert raises(TypeError, lambda: bt.add_sub_tree(None))
bt1 = BinomialTree(BinomialTreeNode(1, 1), 0)
node = BinomialTreeNode(2, 2)
node.add_children(BinomialTreeNode(3, 3))
bt2 = BinomialTree(node, 1)
assert raises(ValueError, lambda: bt1.add_sub_tree(bt2))
assert bt1.is_empty is False
3 changes: 2 additions & 1 deletion pydatastructs/trees/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
__all__.extend(space_partitioning_trees.__all__)

from .heaps import (
BinaryHeap
BinaryHeap,
BinomialHeap
)
__all__.extend(heaps.__all__)
225 changes: 221 additions & 4 deletions pydatastructs/trees/heaps.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
from pydatastructs.utils.misc_util import _check_type, NoneType, TreeNode
from pydatastructs.linear_data_structures.arrays import ArrayForTrees
from pydatastructs.utils.misc_util import _check_type, NoneType, TreeNode, BinomialTreeNode
from pydatastructs.linear_data_structures.arrays import (ArrayForTrees,
DynamicOneDimensionalArray)
from pydatastructs.miscellaneous_data_structures.binomial_trees import BinomialTree

__all__ = [
'BinaryHeap'
'BinaryHeap',
'BinomialHeap'
]

class Heap:
class Heap(object):
"""
Abstract class for representing heaps.
"""
Expand Down Expand Up @@ -181,3 +184,217 @@ def __str__(self):
node.key, node.data,
node.right if node.right <= self._last_pos_filled else None)
return str(to_be_printed)


class BinomialHeap(Heap):
"""
Represents binomial heap.
Parameters
==========
root_list: list/tuple
By default, []
The list of BinomialTree object references
in sorted order.
Examples
========
>>> from pydatastructs import BinomialHeap
>>> b = BinomialHeap()
>>> b.insert(1, 1)
>>> b.insert(2, 2)
>>> b.find_minimum().key
1
>>> b.find_minimum().children[0].key
2
References
==========
.. [1] https://en.wikipedia.org/wiki/Binomial_heap
"""
__slots__ = ['root_list']

def __new__(cls, root_list=[]):
if not all((_check_type(root, BinomialTree))
for root in root_list):
raise TypeError("The root_list should contain "
"references to objects of BinomialTree.")
obj = Heap.__new__(cls)
obj.root_list = DynamicOneDimensionalArray(BinomialTree, root_list)
return obj

def merge_tree(self, tree1, tree2):
"""
Merges two BinomialTree objects.
Parameters
==========
tree1: BinomialTree
tree2: BinomialTree
"""
if (not _check_type(tree1, BinomialTree)) or \
(not _check_type(tree2, BinomialTree)):
raise TypeError("Both the trees should be of type "
"BinomalTree.")
ret_value = None
if tree1.root.key <= tree2.root.key:
tree1.add_sub_tree(tree2)
ret_value = tree1
else:
tree2.add_sub_tree(tree1)
ret_value = tree2
return ret_value

def _merge_heap_last_new_tree(self, new_root_list, new_tree):
"""
Merges last tree node in root list with the incoming tree.
"""
pos = new_root_list._last_pos_filled
if (new_root_list.size != 0) and new_root_list[pos].order == new_tree.order:
new_root_list[pos] = self.merge_tree(new_root_list[pos], new_tree)
else:
new_root_list.append(new_tree)

def merge(self, other_heap):
"""
Merges current binomial heap with the given binomial heap.
Parameters
==========
other_heap: BinomialHeap
"""
if not _check_type(other_heap, BinomialHeap):
raise TypeError("Other heap is not of type BinomialHeap.")
new_root_list = DynamicOneDimensionalArray(BinomialTree, 0)
i, j = 0, 0
while ((i <= self.root_list._last_pos_filled) and
(j <= other_heap.root_list._last_pos_filled)):
new_tree = None
while self.root_list[i] is None:
i += 1
while other_heap.root_list[j] is None:
j += 1
if self.root_list[i].order == other_heap.root_list[j].order:
new_tree = self.merge_tree(self.root_list[i],
other_heap.root_list[j])
i += 1
j += 1
else:
if self.root_list[i].order < other_heap.root_list[j].order:
new_tree = self.root_list[i]
i += 1
else:
new_tree = other_heap.root_list[j]
j += 1
self._merge_heap_last_new_tree(new_root_list, new_tree)

while i <= self.root_list._last_pos_filled:
new_tree = self.root_list[i]
self._merge_heap_last_new_tree(new_root_list, new_tree)
i += 1
while j <= other_heap.root_list._last_pos_filled:
new_tree = other_heap.root_list[j]
self._merge_heap_last_new_tree(new_root_list, new_tree)
j += 1
self.root_list = new_root_list

def insert(self, key, data):
"""
Inserts new node with the given key and data.
key
The key of the node which can be operated
upon by relational operators.
data
The data to be stored in the new node.
"""
new_node = BinomialTreeNode(key, data)
new_tree = BinomialTree(root=new_node, order=0)
new_heap = BinomialHeap(root_list=[new_tree])
self.merge(new_heap)

def find_minimum(self, **kwargs):
"""
Finds the node with the minimum key.
Returns
=======
min_node: BinomialTreeNode
"""
if self.is_empty:
raise ValueError("Binomial heap is empty.")
min_node = None
idx, min_idx = 0, None
for tree in self.root_list:
if ((min_node is None) or
(tree is not None and tree.root is not None and
min_node.key > tree.root.key)):
min_node = tree.root
min_idx = idx
idx += 1
if kwargs.get('get_index', None) is not None:
return min_node, min_idx
return min_node

def delete_minimum(self):
"""
Deletes the node with minimum key.
"""
min_node, min_idx = self.find_minimum(get_index=True)
child_root_list = []
for k, child in enumerate(min_node.children):
if child is not None:
child_root_list.append(BinomialTree(root=child, order=k))
self.root_list.delete(min_idx)
child_heap = BinomialHeap(root_list=child_root_list)
self.merge(child_heap)

@property
def is_empty(self):
return self.root_list._last_pos_filled == -1

def decrease_key(self, node, new_key):
"""
Decreases the key of the given node.
Parameters
==========
node: BinomialTreeNode
The node whose key is to be reduced.
new_key
The new key of the given node,
should be less than the current key.
"""
if node.key <= new_key:
raise ValueError("The new key "
"should be less than current node's key.")
node.key = new_key
while ((not node.is_root) and
(node.parent.key > node.key)):
node.parent.key, node.key = \
node.key, node.parent.key
node.parent.data, node.data = \
node.data, node.parent.data
node = node.parent

def delete(self, node):
"""
Deletes the given node.
Parameters
==========
node: BinomialTreeNode
The node which is to be deleted.
"""
self.decrease_key(node, self.find_minimum().key - 1)
self.delete_minimum()
Loading

0 comments on commit b2e3900

Please sign in to comment.