Skip to content

Commit

Permalink
[WIP] Added binary tree traversals (#21)
Browse files Browse the repository at this point in the history
* Added BinaryTreeTraversal

* `peek` and `is_converge` added to stack
  • Loading branch information
czgdp1807 authored Jun 28, 2019
1 parent e6bdeb8 commit 5980c91
Show file tree
Hide file tree
Showing 6 changed files with 264 additions and 4 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,6 @@ __pycache__/
.pytest_cache/
pre_commit.ps1
.coverage
htmlcov
# for developement purposes
pds_debug.py
18 changes: 17 additions & 1 deletion pydatastructs/miscellaneous_data_structures/stack.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ def __new__(cls, implementation='array', **kwargs):
if implementation == 'array':
return ArrayStack(
kwargs.get('maxsize', None),
kwargs.get('top', None),
kwargs.get('top', 0),
kwargs.get('items', None),
kwargs.get('dtype', int))
raise NotImplementedError(
Expand All @@ -74,6 +74,14 @@ def pop(self, *args, **kwargs):
raise NotImplementedError(
"This is an abstract method.")

@property
def is_empty(self):
return None

@property
def peek(self):
return None

class ArrayStack(Stack):

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

@property
def is_empty(self):
return self.top == 0

@property
def peek(self):
return self.items[self.top - 1]

def __str__(self):
"""
Used for printing.
Expand Down
2 changes: 1 addition & 1 deletion pydatastructs/trees/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
)

from .binary_trees import (
Node, BinaryTree, BinarySearchTree
Node, BinaryTree, BinarySearchTree, BinaryTreeTraversal
)
__all__.extend(binary_trees.__all__)

Expand Down
198 changes: 197 additions & 1 deletion pydatastructs/trees/binary_trees.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
from __future__ import print_function, division
from pydatastructs.utils import Node
from pydatastructs.miscellaneous_data_structures import Stack
from pydatastructs.linear_data_structures import OneDimensionalArray
# TODO: REPLACE COLLECTIONS QUEUE WITH PYDATASTRUCTS QUEUE
from collections import deque as Queue

__all__ = [
'Node',
'BinaryTree',
'BinarySearchTree'
'BinarySearchTree',
'BinaryTreeTraversal'
]

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

return True

class BinaryTreeTraversal(object):
"""
Represents the traversals possible in
a binary tree.
Parameters
==========
tree: BinaryTree
The binary tree for whose traversal
is to be done.
Traversals
==========
- Depth First Search
In Order, Post Order, Pre Order Out Order
- Breadth First Search
Examples
========
>>> from pydatastructs import BinarySearchTree as BST
>>> from pydatastructs import BinaryTreeTraversal as BTT
>>> b = BST(2, 2)
>>> b.insert(1, 1)
>>> b.insert(3, 3)
>>> trav = BTT(b)
>>> dfs = trav.depth_first_search()
>>> [str(n) for n in dfs]
['(None, 1, 1, None)', '(1, 2, 2, 2)', '(None, 3, 3, None)']
>>> bfs = trav.breadth_first_search()
>>> [str(n) for n in bfs]
['(1, 2, 2, 2)', '(None, 1, 1, None)', '(None, 3, 3, None)']
References
==========
.. [1] https://en.wikipedia.org/wiki/Tree_traversal
"""

__slots__ = ['tree']

def __new__(cls, tree):
if not isinstance(tree, BinaryTree):
raise TypeError("%s is not a binary tree"%(tree))
obj = object.__new__(cls)
obj.tree = tree
return obj

def _pre_order(self, node):
"""
Utility method for computing pre-order
of a binary tree using iterative algorithm.
"""
visit = []
if node == None:
return visit
tree, size = self.tree.tree, self.tree.size
s = Stack(maxsize=size)
s.push(node)
while not s.is_empty:
node = s.pop()
visit.append(tree[node])
if tree[node].right != None:
s.push(tree[node].right)
if tree[node].left != None:
s.push(tree[node].left)
return visit

def _in_order(self, node):
"""
Utility method for computing in-order
of a binary tree using iterative algorithm.
"""
visit = []
tree, size = self.tree.tree, self.tree.size
s = Stack(maxsize=size)
while not s.is_empty or node != None:
if node != None:
s.push(node)
node = tree[node].left
else:
node = s.pop()
visit.append(tree[node])
node = tree[node].right
return visit

def _post_order(self, node):
"""
Utility method for computing post-order
of a binary tree using iterative algorithm.
"""
visit = []
tree, size = self.tree.tree, self.tree.size
s = Stack(maxsize=size)
s.push(node)
last = OneDimensionalArray(int, size)
last.fill(False)
while not s.is_empty:
node = s.peek
l, r = tree[node].left, tree[node].right
cl, cr = l == None or last[l], r == None or last[r]
if cl and cr:
s.pop()
visit.append(tree[node])
last[node] = True
continue
if not cr:
s.push(r)
if not cl:
s.push(l)
return visit

def _out_order(self, node):
"""
Utility method for computing out-order
of a binary tree using iterative algorithm.
"""
return reversed(self._in_order(node))

def depth_first_search(self, order='in_order', node=None):
"""
Computes the depth first search traversal of the binary
trees.
Parameters
==========
order : str
One of the strings, 'in_order', 'post_order',
'pre_order', 'out_order'.
By default, it is set to, 'in_order'.
node : int
The index of the node from where the traversal
is to be instantiated.
Returns
=======
list
Each element is of type 'Node'.
"""
if node == None:
node = self.tree.root_idx
if order not in ('in_order', 'post_order', 'pre_order', 'out_order'):
raise NotImplementedError(
"%s order is not implemented yet."
"We only support `in_order`, `post_order`, "
"`pre_order` and `out_order` traversals.")
return getattr(self, '_' + order)(node)

def breadth_first_search(self, node=None, strategy='queue'):
# TODO: IMPLEMENT ITERATIVE DEEPENING-DEPTH FIRST SEARCH STRATEGY
"""
Computes the breadth first search traversal of a binary tree.
Parameters
==========
strategy : str
The strategy using which the computation has to happen.
By default, it is set 'queue'.
node : int
The index of the node from where the traversal has to be instantiated.
By default, set to, root index.
Returns
=======
list
Each element of the list is of type `Node`.
"""
strategies = ('queue',)
if strategy not in strategies:
raise NotImplementedError(
"%s startegy is not implemented yet"%(strategy))
if node == None:
node = self.tree.root_idx
q, visit, tree = Queue(), [], self.tree.tree
q.append(node)
while len(q) > 0:
node = q.popleft()
visit.append(tree[node])
if tree[node].left != None:
q.append(tree[node].left)
if tree[node].right != None:
q.append(tree[node].right)
return visit
1 change: 1 addition & 0 deletions pydatastructs/trees/space_partitioning_trees.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from pydatastructs.utils import Node
# TODO: REPLACE COLLECTIONS QUEUE WITH PYDATASTRUCTS QUEUE
from collections import deque as Queue
from pydatastructs.linear_data_structures.arrays import _check_type

Expand Down
46 changes: 45 additions & 1 deletion pydatastructs/trees/tests/test_binary_trees.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from pydatastructs.trees.binary_trees import BinarySearchTree
from pydatastructs.trees.binary_trees import (
BinarySearchTree, BinaryTreeTraversal)
from pydatastructs.utils.raises_util import raises

def test_BinarySearchTree():
Expand Down Expand Up @@ -32,3 +33,46 @@ def test_BinarySearchTree():
bc = BST(1, 1)
assert bc.insert(1, 2) == None
raises(ValueError, lambda: BST(root_data=6))

def test_BinaryTreeTraversal():
BST = BinarySearchTree
BTT = BinaryTreeTraversal
b = BST('F', 'F')
b.insert('B', 'B')
b.insert('A', 'A')
b.insert('G', 'G')
b.insert('D', 'D')
b.insert('C', 'C')
b.insert('E', 'E')
b.insert('I', 'I')
b.insert('H', 'H')
trav = BTT(b)
pre = trav.depth_first_search(order='pre_order')
assert [str(n) for n in pre] == \
["(1, 'F', 'F', 3)", "(2, 'B', 'B', 4)", "(None, 'A', 'A', None)",
"(5, 'D', 'D', 6)", "(None, 'C', 'C', None)", "(None, 'E', 'E', None)",
"(None, 'G', 'G', 7)", "(8, 'I', 'I', None)", "(None, 'H', 'H', None)"]
ino = trav.depth_first_search()
assert [str(n) for n in ino] == \
["(None, 'A', 'A', None)", "(2, 'B', 'B', 4)", "(None, 'C', 'C', None)",
"(5, 'D', 'D', 6)", "(None, 'E', 'E', None)", "(1, 'F', 'F', 3)",
"(None, 'G', 'G', 7)", "(None, 'H', 'H', None)", "(8, 'I', 'I', None)"]
out = trav.depth_first_search(order='out_order')
assert [str(n) for n in out] == \
["(8, 'I', 'I', None)", "(None, 'H', 'H', None)", "(None, 'G', 'G', 7)",
"(1, 'F', 'F', 3)", "(None, 'E', 'E', None)", "(5, 'D', 'D', 6)",
"(None, 'C', 'C', None)", "(2, 'B', 'B', 4)", "(None, 'A', 'A', None)"]
post = trav.depth_first_search(order='post_order')
assert [str(n) for n in post] == \
["(None, 'A', 'A', None)", "(None, 'C', 'C', None)",
"(None, 'E', 'E', None)", "(5, 'D', 'D', 6)", "(2, 'B', 'B', 4)",
"(None, 'H', 'H', None)", "(8, 'I', 'I', None)", "(None, 'G', 'G', 7)",
"(1, 'F', 'F', 3)"]
bfs = trav.breadth_first_search()
assert [str(n) for n in bfs] == \
["(1, 'F', 'F', 3)", "(2, 'B', 'B', 4)", "(None, 'G', 'G', 7)",
"(None, 'A', 'A', None)", "(5, 'D', 'D', 6)", "(8, 'I', 'I', None)",
"(None, 'C', 'C', None)", "(None, 'E', 'E', None)",
"(None, 'H', 'H', None)"]
raises(NotImplementedError, lambda: trav.breadth_first_search(strategy='iddfs'))
raises(NotImplementedError, lambda: trav.depth_first_search(order='in_out_order'))

0 comments on commit 5980c91

Please sign in to comment.