Skip to content

Commit b2e3900

Browse files
authored
[WIP] Added Binomial Heap (#68)
* added BinomialTreeNode * added BinomialTree * added BinomialHeap
1 parent d754853 commit b2e3900

File tree

8 files changed

+485
-15
lines changed

8 files changed

+485
-15
lines changed

pydatastructs/miscellaneous_data_structures/__init__.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,14 @@
22

33
from . import (
44
stack,
5+
binomial_trees
56
)
67

8+
from .binomial_trees import (
9+
BinomialTree
10+
)
11+
__all__.extend(binomial_trees.__all__)
12+
713
from .stack import (
814
Stack,
915
)
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
from pydatastructs.utils.misc_util import BinomialTreeNode, _check_type
2+
3+
__all__ = [
4+
'BinomialTree'
5+
]
6+
7+
class BinomialTree(object):
8+
"""
9+
Represents binomial trees
10+
11+
Parameters
12+
==========
13+
14+
root: BinomialTreeNode
15+
The root of the binomial tree.
16+
By default, None
17+
order: int
18+
The order of the binomial tree.
19+
By default, None
20+
21+
Examples
22+
========
23+
24+
>>> from pydatastructs import BinomialTree, BinomialTreeNode
25+
>>> root = BinomialTreeNode(1, 1)
26+
>>> tree = BinomialTree(root, 0)
27+
>>> tree.is_empty
28+
False
29+
30+
References
31+
==========
32+
33+
.. [1] https://en.wikipedia.org/wiki/Binomial_heap
34+
"""
35+
__slots__ = ['root', 'order']
36+
37+
def __new__(cls, root=None, order=None):
38+
if root is not None and \
39+
not _check_type(root, BinomialTreeNode):
40+
raise TypeError("%s i.e., root should be of "
41+
"type BinomialTreeNode."%(root))
42+
if order is not None and not _check_type(order, int):
43+
raise TypeError("%s i.e., order should be of "
44+
"type int."%(order))
45+
obj = object.__new__(cls)
46+
if root is not None:
47+
root.is_root = True
48+
obj.root = root
49+
obj.order = order
50+
return obj
51+
52+
def add_sub_tree(self, other_tree):
53+
"""
54+
Adds a sub tree to current tree.
55+
56+
Parameters
57+
==========
58+
59+
other_tree: BinomialTree
60+
61+
Raises
62+
======
63+
64+
ValueError: If order of the two trees
65+
are different.
66+
"""
67+
if not _check_type(other_tree, BinomialTree):
68+
raise TypeError("%s i.e., other_tree should be of "
69+
"type BinomialTree"%(other_tree))
70+
if self.order != other_tree.order:
71+
raise ValueError("Orders of both the trees should be same.")
72+
self.root.children.append(other_tree.root)
73+
other_tree.root.parent = self.root
74+
other_tree.root.is_root = False
75+
self.order += 1
76+
77+
@property
78+
def is_empty(self):
79+
return self.root is None
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
from pydatastructs.miscellaneous_data_structures.binomial_trees import BinomialTree
2+
from pydatastructs.utils.raises_util import raises
3+
from pydatastructs.utils.misc_util import BinomialTreeNode
4+
5+
# only tests the corner cases
6+
def test_BinomialTree():
7+
assert raises(TypeError, lambda: BinomialTree(1, 1))
8+
assert raises(TypeError, lambda: BinomialTree(None, 1.5))
9+
10+
bt = BinomialTree()
11+
assert raises(TypeError, lambda: bt.add_sub_tree(None))
12+
bt1 = BinomialTree(BinomialTreeNode(1, 1), 0)
13+
node = BinomialTreeNode(2, 2)
14+
node.add_children(BinomialTreeNode(3, 3))
15+
bt2 = BinomialTree(node, 1)
16+
assert raises(ValueError, lambda: bt1.add_sub_tree(bt2))
17+
assert bt1.is_empty is False

pydatastructs/trees/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
__all__.extend(space_partitioning_trees.__all__)
1717

1818
from .heaps import (
19-
BinaryHeap
19+
BinaryHeap,
20+
BinomialHeap
2021
)
2122
__all__.extend(heaps.__all__)

pydatastructs/trees/heaps.py

Lines changed: 221 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
1-
from pydatastructs.utils.misc_util import _check_type, NoneType, TreeNode
2-
from pydatastructs.linear_data_structures.arrays import ArrayForTrees
1+
from pydatastructs.utils.misc_util import _check_type, NoneType, TreeNode, BinomialTreeNode
2+
from pydatastructs.linear_data_structures.arrays import (ArrayForTrees,
3+
DynamicOneDimensionalArray)
4+
from pydatastructs.miscellaneous_data_structures.binomial_trees import BinomialTree
35

46
__all__ = [
5-
'BinaryHeap'
7+
'BinaryHeap',
8+
'BinomialHeap'
69
]
710

8-
class Heap:
11+
class Heap(object):
912
"""
1013
Abstract class for representing heaps.
1114
"""
@@ -181,3 +184,217 @@ def __str__(self):
181184
node.key, node.data,
182185
node.right if node.right <= self._last_pos_filled else None)
183186
return str(to_be_printed)
187+
188+
189+
class BinomialHeap(Heap):
190+
"""
191+
Represents binomial heap.
192+
193+
Parameters
194+
==========
195+
196+
root_list: list/tuple
197+
By default, []
198+
The list of BinomialTree object references
199+
in sorted order.
200+
201+
Examples
202+
========
203+
204+
>>> from pydatastructs import BinomialHeap
205+
>>> b = BinomialHeap()
206+
>>> b.insert(1, 1)
207+
>>> b.insert(2, 2)
208+
>>> b.find_minimum().key
209+
1
210+
>>> b.find_minimum().children[0].key
211+
2
212+
213+
References
214+
==========
215+
216+
.. [1] https://en.wikipedia.org/wiki/Binomial_heap
217+
"""
218+
__slots__ = ['root_list']
219+
220+
def __new__(cls, root_list=[]):
221+
if not all((_check_type(root, BinomialTree))
222+
for root in root_list):
223+
raise TypeError("The root_list should contain "
224+
"references to objects of BinomialTree.")
225+
obj = Heap.__new__(cls)
226+
obj.root_list = DynamicOneDimensionalArray(BinomialTree, root_list)
227+
return obj
228+
229+
def merge_tree(self, tree1, tree2):
230+
"""
231+
Merges two BinomialTree objects.
232+
233+
Parameters
234+
==========
235+
236+
tree1: BinomialTree
237+
238+
tree2: BinomialTree
239+
"""
240+
if (not _check_type(tree1, BinomialTree)) or \
241+
(not _check_type(tree2, BinomialTree)):
242+
raise TypeError("Both the trees should be of type "
243+
"BinomalTree.")
244+
ret_value = None
245+
if tree1.root.key <= tree2.root.key:
246+
tree1.add_sub_tree(tree2)
247+
ret_value = tree1
248+
else:
249+
tree2.add_sub_tree(tree1)
250+
ret_value = tree2
251+
return ret_value
252+
253+
def _merge_heap_last_new_tree(self, new_root_list, new_tree):
254+
"""
255+
Merges last tree node in root list with the incoming tree.
256+
"""
257+
pos = new_root_list._last_pos_filled
258+
if (new_root_list.size != 0) and new_root_list[pos].order == new_tree.order:
259+
new_root_list[pos] = self.merge_tree(new_root_list[pos], new_tree)
260+
else:
261+
new_root_list.append(new_tree)
262+
263+
def merge(self, other_heap):
264+
"""
265+
Merges current binomial heap with the given binomial heap.
266+
267+
Parameters
268+
==========
269+
270+
other_heap: BinomialHeap
271+
"""
272+
if not _check_type(other_heap, BinomialHeap):
273+
raise TypeError("Other heap is not of type BinomialHeap.")
274+
new_root_list = DynamicOneDimensionalArray(BinomialTree, 0)
275+
i, j = 0, 0
276+
while ((i <= self.root_list._last_pos_filled) and
277+
(j <= other_heap.root_list._last_pos_filled)):
278+
new_tree = None
279+
while self.root_list[i] is None:
280+
i += 1
281+
while other_heap.root_list[j] is None:
282+
j += 1
283+
if self.root_list[i].order == other_heap.root_list[j].order:
284+
new_tree = self.merge_tree(self.root_list[i],
285+
other_heap.root_list[j])
286+
i += 1
287+
j += 1
288+
else:
289+
if self.root_list[i].order < other_heap.root_list[j].order:
290+
new_tree = self.root_list[i]
291+
i += 1
292+
else:
293+
new_tree = other_heap.root_list[j]
294+
j += 1
295+
self._merge_heap_last_new_tree(new_root_list, new_tree)
296+
297+
while i <= self.root_list._last_pos_filled:
298+
new_tree = self.root_list[i]
299+
self._merge_heap_last_new_tree(new_root_list, new_tree)
300+
i += 1
301+
while j <= other_heap.root_list._last_pos_filled:
302+
new_tree = other_heap.root_list[j]
303+
self._merge_heap_last_new_tree(new_root_list, new_tree)
304+
j += 1
305+
self.root_list = new_root_list
306+
307+
def insert(self, key, data):
308+
"""
309+
Inserts new node with the given key and data.
310+
311+
key
312+
The key of the node which can be operated
313+
upon by relational operators.
314+
315+
data
316+
The data to be stored in the new node.
317+
"""
318+
new_node = BinomialTreeNode(key, data)
319+
new_tree = BinomialTree(root=new_node, order=0)
320+
new_heap = BinomialHeap(root_list=[new_tree])
321+
self.merge(new_heap)
322+
323+
def find_minimum(self, **kwargs):
324+
"""
325+
Finds the node with the minimum key.
326+
327+
Returns
328+
=======
329+
330+
min_node: BinomialTreeNode
331+
"""
332+
if self.is_empty:
333+
raise ValueError("Binomial heap is empty.")
334+
min_node = None
335+
idx, min_idx = 0, None
336+
for tree in self.root_list:
337+
if ((min_node is None) or
338+
(tree is not None and tree.root is not None and
339+
min_node.key > tree.root.key)):
340+
min_node = tree.root
341+
min_idx = idx
342+
idx += 1
343+
if kwargs.get('get_index', None) is not None:
344+
return min_node, min_idx
345+
return min_node
346+
347+
def delete_minimum(self):
348+
"""
349+
Deletes the node with minimum key.
350+
"""
351+
min_node, min_idx = self.find_minimum(get_index=True)
352+
child_root_list = []
353+
for k, child in enumerate(min_node.children):
354+
if child is not None:
355+
child_root_list.append(BinomialTree(root=child, order=k))
356+
self.root_list.delete(min_idx)
357+
child_heap = BinomialHeap(root_list=child_root_list)
358+
self.merge(child_heap)
359+
360+
@property
361+
def is_empty(self):
362+
return self.root_list._last_pos_filled == -1
363+
364+
def decrease_key(self, node, new_key):
365+
"""
366+
Decreases the key of the given node.
367+
368+
Parameters
369+
==========
370+
371+
node: BinomialTreeNode
372+
The node whose key is to be reduced.
373+
new_key
374+
The new key of the given node,
375+
should be less than the current key.
376+
"""
377+
if node.key <= new_key:
378+
raise ValueError("The new key "
379+
"should be less than current node's key.")
380+
node.key = new_key
381+
while ((not node.is_root) and
382+
(node.parent.key > node.key)):
383+
node.parent.key, node.key = \
384+
node.key, node.parent.key
385+
node.parent.data, node.data = \
386+
node.data, node.parent.data
387+
node = node.parent
388+
389+
def delete(self, node):
390+
"""
391+
Deletes the given node.
392+
393+
Parameters
394+
==========
395+
396+
node: BinomialTreeNode
397+
The node which is to be deleted.
398+
"""
399+
self.decrease_key(node, self.find_minimum().key - 1)
400+
self.delete_minimum()

0 commit comments

Comments
 (0)