Skip to content

Commit 5980c91

Browse files
authored
[WIP] Added binary tree traversals (#21)
* Added BinaryTreeTraversal * `peek` and `is_converge` added to stack
1 parent e6bdeb8 commit 5980c91

File tree

6 files changed

+264
-4
lines changed

6 files changed

+264
-4
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,6 @@ __pycache__/
66
.pytest_cache/
77
pre_commit.ps1
88
.coverage
9+
htmlcov
10+
# for developement purposes
11+
pds_debug.py

pydatastructs/miscellaneous_data_structures/stack.py

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ def __new__(cls, implementation='array', **kwargs):
6060
if implementation == 'array':
6161
return ArrayStack(
6262
kwargs.get('maxsize', None),
63-
kwargs.get('top', None),
63+
kwargs.get('top', 0),
6464
kwargs.get('items', None),
6565
kwargs.get('dtype', int))
6666
raise NotImplementedError(
@@ -74,6 +74,14 @@ def pop(self, *args, **kwargs):
7474
raise NotImplementedError(
7575
"This is an abstract method.")
7676

77+
@property
78+
def is_empty(self):
79+
return None
80+
81+
@property
82+
def peek(self):
83+
return None
84+
7785
class ArrayStack(Stack):
7886

7987
def __new__(cls, maxsize=None, top=0, items=None, dtype=int):
@@ -107,6 +115,14 @@ def pop(self):
107115
self.items[self.top] = None
108116
return r
109117

118+
@property
119+
def is_empty(self):
120+
return self.top == 0
121+
122+
@property
123+
def peek(self):
124+
return self.items[self.top - 1]
125+
110126
def __str__(self):
111127
"""
112128
Used for printing.

pydatastructs/trees/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
)
77

88
from .binary_trees import (
9-
Node, BinaryTree, BinarySearchTree
9+
Node, BinaryTree, BinarySearchTree, BinaryTreeTraversal
1010
)
1111
__all__.extend(binary_trees.__all__)
1212

pydatastructs/trees/binary_trees.py

Lines changed: 197 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,15 @@
11
from __future__ import print_function, division
22
from pydatastructs.utils import Node
3+
from pydatastructs.miscellaneous_data_structures import Stack
4+
from pydatastructs.linear_data_structures import OneDimensionalArray
5+
# TODO: REPLACE COLLECTIONS QUEUE WITH PYDATASTRUCTS QUEUE
6+
from collections import deque as Queue
37

48
__all__ = [
59
'Node',
610
'BinaryTree',
7-
'BinarySearchTree'
11+
'BinarySearchTree',
12+
'BinaryTreeTraversal'
813
]
914

1015
class BinaryTree(object):
@@ -243,3 +248,194 @@ def delete(self, key):
243248
self.tree[parent].right = child
244249

245250
return True
251+
252+
class BinaryTreeTraversal(object):
253+
"""
254+
Represents the traversals possible in
255+
a binary tree.
256+
257+
Parameters
258+
==========
259+
260+
tree: BinaryTree
261+
The binary tree for whose traversal
262+
is to be done.
263+
264+
Traversals
265+
==========
266+
267+
- Depth First Search
268+
In Order, Post Order, Pre Order Out Order
269+
270+
- Breadth First Search
271+
272+
Examples
273+
========
274+
275+
>>> from pydatastructs import BinarySearchTree as BST
276+
>>> from pydatastructs import BinaryTreeTraversal as BTT
277+
>>> b = BST(2, 2)
278+
>>> b.insert(1, 1)
279+
>>> b.insert(3, 3)
280+
>>> trav = BTT(b)
281+
>>> dfs = trav.depth_first_search()
282+
>>> [str(n) for n in dfs]
283+
['(None, 1, 1, None)', '(1, 2, 2, 2)', '(None, 3, 3, None)']
284+
>>> bfs = trav.breadth_first_search()
285+
>>> [str(n) for n in bfs]
286+
['(1, 2, 2, 2)', '(None, 1, 1, None)', '(None, 3, 3, None)']
287+
288+
References
289+
==========
290+
291+
.. [1] https://en.wikipedia.org/wiki/Tree_traversal
292+
"""
293+
294+
__slots__ = ['tree']
295+
296+
def __new__(cls, tree):
297+
if not isinstance(tree, BinaryTree):
298+
raise TypeError("%s is not a binary tree"%(tree))
299+
obj = object.__new__(cls)
300+
obj.tree = tree
301+
return obj
302+
303+
def _pre_order(self, node):
304+
"""
305+
Utility method for computing pre-order
306+
of a binary tree using iterative algorithm.
307+
"""
308+
visit = []
309+
if node == None:
310+
return visit
311+
tree, size = self.tree.tree, self.tree.size
312+
s = Stack(maxsize=size)
313+
s.push(node)
314+
while not s.is_empty:
315+
node = s.pop()
316+
visit.append(tree[node])
317+
if tree[node].right != None:
318+
s.push(tree[node].right)
319+
if tree[node].left != None:
320+
s.push(tree[node].left)
321+
return visit
322+
323+
def _in_order(self, node):
324+
"""
325+
Utility method for computing in-order
326+
of a binary tree using iterative algorithm.
327+
"""
328+
visit = []
329+
tree, size = self.tree.tree, self.tree.size
330+
s = Stack(maxsize=size)
331+
while not s.is_empty or node != None:
332+
if node != None:
333+
s.push(node)
334+
node = tree[node].left
335+
else:
336+
node = s.pop()
337+
visit.append(tree[node])
338+
node = tree[node].right
339+
return visit
340+
341+
def _post_order(self, node):
342+
"""
343+
Utility method for computing post-order
344+
of a binary tree using iterative algorithm.
345+
"""
346+
visit = []
347+
tree, size = self.tree.tree, self.tree.size
348+
s = Stack(maxsize=size)
349+
s.push(node)
350+
last = OneDimensionalArray(int, size)
351+
last.fill(False)
352+
while not s.is_empty:
353+
node = s.peek
354+
l, r = tree[node].left, tree[node].right
355+
cl, cr = l == None or last[l], r == None or last[r]
356+
if cl and cr:
357+
s.pop()
358+
visit.append(tree[node])
359+
last[node] = True
360+
continue
361+
if not cr:
362+
s.push(r)
363+
if not cl:
364+
s.push(l)
365+
return visit
366+
367+
def _out_order(self, node):
368+
"""
369+
Utility method for computing out-order
370+
of a binary tree using iterative algorithm.
371+
"""
372+
return reversed(self._in_order(node))
373+
374+
def depth_first_search(self, order='in_order', node=None):
375+
"""
376+
Computes the depth first search traversal of the binary
377+
trees.
378+
379+
Parameters
380+
==========
381+
382+
order : str
383+
One of the strings, 'in_order', 'post_order',
384+
'pre_order', 'out_order'.
385+
By default, it is set to, 'in_order'.
386+
node : int
387+
The index of the node from where the traversal
388+
is to be instantiated.
389+
390+
Returns
391+
=======
392+
393+
list
394+
Each element is of type 'Node'.
395+
"""
396+
if node == None:
397+
node = self.tree.root_idx
398+
if order not in ('in_order', 'post_order', 'pre_order', 'out_order'):
399+
raise NotImplementedError(
400+
"%s order is not implemented yet."
401+
"We only support `in_order`, `post_order`, "
402+
"`pre_order` and `out_order` traversals.")
403+
return getattr(self, '_' + order)(node)
404+
405+
def breadth_first_search(self, node=None, strategy='queue'):
406+
# TODO: IMPLEMENT ITERATIVE DEEPENING-DEPTH FIRST SEARCH STRATEGY
407+
"""
408+
Computes the breadth first search traversal of a binary tree.
409+
410+
Parameters
411+
==========
412+
413+
strategy : str
414+
The strategy using which the computation has to happen.
415+
By default, it is set 'queue'.
416+
node : int
417+
The index of the node from where the traversal has to be instantiated.
418+
By default, set to, root index.
419+
420+
Returns
421+
=======
422+
423+
list
424+
Each element of the list is of type `Node`.
425+
"""
426+
strategies = ('queue',)
427+
if strategy not in strategies:
428+
raise NotImplementedError(
429+
"%s startegy is not implemented yet"%(strategy))
430+
if node == None:
431+
node = self.tree.root_idx
432+
q, visit, tree = Queue(), [], self.tree.tree
433+
q.append(node)
434+
while len(q) > 0:
435+
node = q.popleft()
436+
visit.append(tree[node])
437+
if tree[node].left != None:
438+
q.append(tree[node].left)
439+
if tree[node].right != None:
440+
q.append(tree[node].right)
441+
return visit

pydatastructs/trees/space_partitioning_trees.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
from pydatastructs.utils import Node
2+
# TODO: REPLACE COLLECTIONS QUEUE WITH PYDATASTRUCTS QUEUE
23
from collections import deque as Queue
34
from pydatastructs.linear_data_structures.arrays import _check_type
45

pydatastructs/trees/tests/test_binary_trees.py

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
from pydatastructs.trees.binary_trees import BinarySearchTree
1+
from pydatastructs.trees.binary_trees import (
2+
BinarySearchTree, BinaryTreeTraversal)
23
from pydatastructs.utils.raises_util import raises
34

45
def test_BinarySearchTree():
@@ -32,3 +33,46 @@ def test_BinarySearchTree():
3233
bc = BST(1, 1)
3334
assert bc.insert(1, 2) == None
3435
raises(ValueError, lambda: BST(root_data=6))
36+
37+
def test_BinaryTreeTraversal():
38+
BST = BinarySearchTree
39+
BTT = BinaryTreeTraversal
40+
b = BST('F', 'F')
41+
b.insert('B', 'B')
42+
b.insert('A', 'A')
43+
b.insert('G', 'G')
44+
b.insert('D', 'D')
45+
b.insert('C', 'C')
46+
b.insert('E', 'E')
47+
b.insert('I', 'I')
48+
b.insert('H', 'H')
49+
trav = BTT(b)
50+
pre = trav.depth_first_search(order='pre_order')
51+
assert [str(n) for n in pre] == \
52+
["(1, 'F', 'F', 3)", "(2, 'B', 'B', 4)", "(None, 'A', 'A', None)",
53+
"(5, 'D', 'D', 6)", "(None, 'C', 'C', None)", "(None, 'E', 'E', None)",
54+
"(None, 'G', 'G', 7)", "(8, 'I', 'I', None)", "(None, 'H', 'H', None)"]
55+
ino = trav.depth_first_search()
56+
assert [str(n) for n in ino] == \
57+
["(None, 'A', 'A', None)", "(2, 'B', 'B', 4)", "(None, 'C', 'C', None)",
58+
"(5, 'D', 'D', 6)", "(None, 'E', 'E', None)", "(1, 'F', 'F', 3)",
59+
"(None, 'G', 'G', 7)", "(None, 'H', 'H', None)", "(8, 'I', 'I', None)"]
60+
out = trav.depth_first_search(order='out_order')
61+
assert [str(n) for n in out] == \
62+
["(8, 'I', 'I', None)", "(None, 'H', 'H', None)", "(None, 'G', 'G', 7)",
63+
"(1, 'F', 'F', 3)", "(None, 'E', 'E', None)", "(5, 'D', 'D', 6)",
64+
"(None, 'C', 'C', None)", "(2, 'B', 'B', 4)", "(None, 'A', 'A', None)"]
65+
post = trav.depth_first_search(order='post_order')
66+
assert [str(n) for n in post] == \
67+
["(None, 'A', 'A', None)", "(None, 'C', 'C', None)",
68+
"(None, 'E', 'E', None)", "(5, 'D', 'D', 6)", "(2, 'B', 'B', 4)",
69+
"(None, 'H', 'H', None)", "(8, 'I', 'I', None)", "(None, 'G', 'G', 7)",
70+
"(1, 'F', 'F', 3)"]
71+
bfs = trav.breadth_first_search()
72+
assert [str(n) for n in bfs] == \
73+
["(1, 'F', 'F', 3)", "(2, 'B', 'B', 4)", "(None, 'G', 'G', 7)",
74+
"(None, 'A', 'A', None)", "(5, 'D', 'D', 6)", "(8, 'I', 'I', None)",
75+
"(None, 'C', 'C', None)", "(None, 'E', 'E', None)",
76+
"(None, 'H', 'H', None)"]
77+
raises(NotImplementedError, lambda: trav.breadth_first_search(strategy='iddfs'))
78+
raises(NotImplementedError, lambda: trav.depth_first_search(order='in_out_order'))

0 commit comments

Comments
 (0)