From f896c9c76a70ced677e80eadbcfe65a7fe9404eb Mon Sep 17 00:00:00 2001 From: czgdp1807 Date: Thu, 27 Jun 2019 12:11:18 +0530 Subject: [PATCH 1/4] in-order, pre-order code --- .gitignore | 2 + .../miscellaneous_data_structures/stack.py | 18 +++- pydatastructs/trees/__init__.py | 2 +- pydatastructs/trees/binary_trees.py | 90 ++++++++++++++++++- .../trees/tests/test_binary_trees.py | 27 +++++- 5 files changed, 135 insertions(+), 4 deletions(-) diff --git a/.gitignore b/.gitignore index 999995a84..d3c33b290 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,5 @@ __pycache__/ .pytest_cache/ pre_commit.ps1 .coverage +# for developement purposes +pds_debug.py diff --git a/pydatastructs/miscellaneous_data_structures/stack.py b/pydatastructs/miscellaneous_data_structures/stack.py index 79dba575d..bff1d75db 100644 --- a/pydatastructs/miscellaneous_data_structures/stack.py +++ b/pydatastructs/miscellaneous_data_structures/stack.py @@ -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( @@ -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): @@ -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. diff --git a/pydatastructs/trees/__init__.py b/pydatastructs/trees/__init__.py index 91f9f5476..d3516442d 100644 --- a/pydatastructs/trees/__init__.py +++ b/pydatastructs/trees/__init__.py @@ -6,7 +6,7 @@ ) from .binary_trees import ( - Node, BinaryTree, BinarySearchTree + Node, BinaryTree, BinarySearchTree, BinaryTreeTraversal ) __all__.extend(binary_trees.__all__) diff --git a/pydatastructs/trees/binary_trees.py b/pydatastructs/trees/binary_trees.py index f1c396b2c..4f908814e 100644 --- a/pydatastructs/trees/binary_trees.py +++ b/pydatastructs/trees/binary_trees.py @@ -1,10 +1,12 @@ from __future__ import print_function, division from pydatastructs.utils import Node +from pydatastructs.miscellaneous_data_structures import Stack __all__ = [ 'Node', 'BinaryTree', - 'BinarySearchTree' + 'BinarySearchTree', + 'BinaryTreeTraversal' ] class BinaryTree(object): @@ -243,3 +245,89 @@ def delete(self, key): self.tree[parent].right = child return True + +class BinaryTreeTraversal(object): + + __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): + 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): + 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): + visit = [] + tree, size = self.tree.tree, self.tree.size + s = Stack(maxsize=size) + s.push(node) + last, cache = [None, None], 0 + while not s.is_empty: + node = s.peek + l, r = tree[node].left, tree[node].right + cl, cr = l == None or l in last, r == None or r in last + if cl and cr: + s.pop() + visit.append(tree[node]) + last[cache] = node + cache = 1 - cache + continue + if not cr: + s.push(r) + if not cl: + s.push(l) + # last_node_visited = None + # while (not s.is_empty) or node != None: + # if node != None: + # s.push(node) + # node = tree[node].left + # else: + # peek_node = s.peek + # if tree[peek_node].right != None and \ + # last_node_visited != tree[peek_node].right: + # node = tree[peek_node].right + # else: + # visit.append(tree[peek_node]) + # last_node_visited = s.pop() + return visit + + def depth_first_search(self, order='in_order', node=None): + 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) diff --git a/pydatastructs/trees/tests/test_binary_trees.py b/pydatastructs/trees/tests/test_binary_trees.py index 8152105c3..a40a1ef5d 100644 --- a/pydatastructs/trees/tests/test_binary_trees.py +++ b/pydatastructs/trees/tests/test_binary_trees.py @@ -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(): @@ -32,3 +33,27 @@ 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)"] + post = trav.depth_first_search() + assert [str(n) for n in post] == \ + ["(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)"] From 108af51349bb59f87c365a3387b589a91dea80e5 Mon Sep 17 00:00:00 2001 From: czgdp1807 Date: Fri, 28 Jun 2019 11:09:03 +0530 Subject: [PATCH 2/4] traversal code completed --- .gitignore | 1 + pydatastructs/trees/binary_trees.py | 25 ++++++------------- .../trees/tests/test_binary_trees.py | 16 ++++++++++-- 3 files changed, 23 insertions(+), 19 deletions(-) diff --git a/.gitignore b/.gitignore index d3c33b290..ec434c41c 100644 --- a/.gitignore +++ b/.gitignore @@ -6,5 +6,6 @@ __pycache__/ .pytest_cache/ pre_commit.ps1 .coverage +htmlcov # for developement purposes pds_debug.py diff --git a/pydatastructs/trees/binary_trees.py b/pydatastructs/trees/binary_trees.py index 4f908814e..800ea99d6 100644 --- a/pydatastructs/trees/binary_trees.py +++ b/pydatastructs/trees/binary_trees.py @@ -1,6 +1,7 @@ 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 __all__ = [ 'Node', @@ -292,36 +293,26 @@ def _post_order(self, node): tree, size = self.tree.tree, self.tree.size s = Stack(maxsize=size) s.push(node) - last, cache = [None, None], 0 + 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 l in last, r == None or r in last + cl, cr = l == None or last[l], r == None or last[r] if cl and cr: s.pop() visit.append(tree[node]) - last[cache] = node - cache = 1 - cache + last[node] = True continue if not cr: s.push(r) if not cl: s.push(l) - # last_node_visited = None - # while (not s.is_empty) or node != None: - # if node != None: - # s.push(node) - # node = tree[node].left - # else: - # peek_node = s.peek - # if tree[peek_node].right != None and \ - # last_node_visited != tree[peek_node].right: - # node = tree[peek_node].right - # else: - # visit.append(tree[peek_node]) - # last_node_visited = s.pop() return visit + def _out_order(self, node): + return reversed(self._in_order(node)) + def depth_first_search(self, order='in_order', node=None): if node == None: node = self.tree.root_idx diff --git a/pydatastructs/trees/tests/test_binary_trees.py b/pydatastructs/trees/tests/test_binary_trees.py index a40a1ef5d..f7c7ad455 100644 --- a/pydatastructs/trees/tests/test_binary_trees.py +++ b/pydatastructs/trees/tests/test_binary_trees.py @@ -52,8 +52,20 @@ def test_BinaryTreeTraversal(): ["(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)"] - post = trav.depth_first_search() - assert [str(n) for n in post] == \ + 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)"] + raises(NotImplementedError, lambda: trav.depth_first_search(order='in_out_order')) From 5e79ba51cf8470a73a0bb65dda16af566520eb6b Mon Sep 17 00:00:00 2001 From: czgdp1807 Date: Fri, 28 Jun 2019 11:27:25 +0530 Subject: [PATCH 3/4] docs added --- pydatastructs/trees/binary_trees.py | 74 +++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) diff --git a/pydatastructs/trees/binary_trees.py b/pydatastructs/trees/binary_trees.py index 800ea99d6..6d488464e 100644 --- a/pydatastructs/trees/binary_trees.py +++ b/pydatastructs/trees/binary_trees.py @@ -248,6 +248,43 @@ def delete(self, key): 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)'] + + References + ========== + + .. [1] https://en.wikipedia.org/wiki/Tree_traversal + """ __slots__ = ['tree'] @@ -259,6 +296,10 @@ def __new__(cls, 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 @@ -275,6 +316,10 @@ def _pre_order(self, node): 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) @@ -289,6 +334,10 @@ def _in_order(self, node): 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) @@ -311,9 +360,34 @@ def _post_order(self, node): 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'): From 90f493ba2ed39366438ab5870f84ad90c6f531e4 Mon Sep 17 00:00:00 2001 From: czgdp1807 Date: Fri, 28 Jun 2019 12:32:06 +0530 Subject: [PATCH 4/4] bfs traversal completed --- pydatastructs/trees/binary_trees.py | 43 +++++++++++++++++++ .../trees/space_partitioning_trees.py | 1 + .../trees/tests/test_binary_trees.py | 7 +++ 3 files changed, 51 insertions(+) diff --git a/pydatastructs/trees/binary_trees.py b/pydatastructs/trees/binary_trees.py index 6d488464e..e0bebf5f0 100644 --- a/pydatastructs/trees/binary_trees.py +++ b/pydatastructs/trees/binary_trees.py @@ -2,6 +2,8 @@ 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', @@ -279,6 +281,9 @@ class BinaryTreeTraversal(object): >>> 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 ========== @@ -396,3 +401,41 @@ def depth_first_search(self, order='in_order', node=None): "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 diff --git a/pydatastructs/trees/space_partitioning_trees.py b/pydatastructs/trees/space_partitioning_trees.py index 9a04c4048..a502e0217 100644 --- a/pydatastructs/trees/space_partitioning_trees.py +++ b/pydatastructs/trees/space_partitioning_trees.py @@ -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 diff --git a/pydatastructs/trees/tests/test_binary_trees.py b/pydatastructs/trees/tests/test_binary_trees.py index f7c7ad455..f5a34a6ec 100644 --- a/pydatastructs/trees/tests/test_binary_trees.py +++ b/pydatastructs/trees/tests/test_binary_trees.py @@ -68,4 +68,11 @@ def test_BinaryTreeTraversal(): "(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'))