From 353e34a3e8363c71daefa0e75368b183d914f597 Mon Sep 17 00:00:00 2001 From: Gagandeep Singh Date: Wed, 19 Jun 2019 16:23:47 +0530 Subject: [PATCH] binary-tree added (#5) --- pydatastructs/__init__.py | 1 + .../linear_data_structures/arrays.py | 5 + pydatastructs/trees/__init__.py | 7 + pydatastructs/trees/binary_trees.py | 276 ++++++++++++++++++ pydatastructs/trees/tests/__init__.py | 0 .../trees/tests/test_binary_trees.py | 31 ++ 6 files changed, 320 insertions(+) create mode 100644 pydatastructs/trees/__init__.py create mode 100644 pydatastructs/trees/binary_trees.py create mode 100644 pydatastructs/trees/tests/__init__.py create mode 100644 pydatastructs/trees/tests/test_binary_trees.py diff --git a/pydatastructs/__init__.py b/pydatastructs/__init__.py index dd863b051..1ff5a79b9 100644 --- a/pydatastructs/__init__.py +++ b/pydatastructs/__init__.py @@ -1 +1,2 @@ from .linear_data_structures import * +from .trees import * diff --git a/pydatastructs/linear_data_structures/arrays.py b/pydatastructs/linear_data_structures/arrays.py index 9be930ddc..9db8d205f 100644 --- a/pydatastructs/linear_data_structures/arrays.py +++ b/pydatastructs/linear_data_structures/arrays.py @@ -58,6 +58,11 @@ class OneDimensionalArray(Array): >>> arr[0] = 7.2 >>> arr[0] 7 + + References + ========== + + .. [1] https://en.wikipedia.org/wiki/Array_data_structure#One-dimensional_arrays ''' def __new__(cls, dtype=NoneType, *args, **kwargs): if dtype == NoneType or len(args) not in (1, 2): diff --git a/pydatastructs/trees/__init__.py b/pydatastructs/trees/__init__.py new file mode 100644 index 000000000..bfddf1de4 --- /dev/null +++ b/pydatastructs/trees/__init__.py @@ -0,0 +1,7 @@ +__all__ = [] + +from . import binary_trees +from .binary_trees import ( + Node, BinaryTree, BinarySearchTree +) +__all__.extend(binary_trees.__all__) diff --git a/pydatastructs/trees/binary_trees.py b/pydatastructs/trees/binary_trees.py new file mode 100644 index 000000000..1345485a6 --- /dev/null +++ b/pydatastructs/trees/binary_trees.py @@ -0,0 +1,276 @@ +from __future__ import print_function, division + +__all__ = [ + 'Node', + 'BinaryTree', + 'BinarySearchTree' +] + +class Node(object): + """ + Represents node in trees. + + Parameters + ========== + + data + Any valid data to be stored in the node. + key + Required for comparison operations. + left: int + Optional, index of the left child node. + right: int + Optional, index of the right child node. + """ + + __slots__ = ['key', 'data', 'left', 'right', 'is_root'] + + def __new__(cls, key, data): + obj = object.__new__(cls) + obj.data, obj.key = data, key + obj.left, obj.right = None, None + obj.is_root = False + return obj + + def __str__(self): + """ + Used for printing. + """ + return str((self.left, self.key, self.data, self.right)) + +class BinaryTree(object): + """ + Abstract binary tree. + + Parameters + ========== + + root_data + Optional, the root node of the binary tree. + If not of type Node, it will consider + root as data and a new root node will + be created. + key + Required if tree is to be instantiated with + root otherwise not needed. + comp: lambda + Optional, A lambda function which will be used + for comparison of keys. Should return a + bool value. By default it implements less + than operator. + + References + ========== + + .. [1] https://en.wikipedia.org/wiki/Binary_tree + """ + + __slots__ = ['root_idx', 'comparator', 'tree', 'size'] + + def __new__(cls, key=None, root_data=None, comp=None): + obj = object.__new__(cls) + if key == None and root_data != None: + raise ValueError('Key required.') + key = None if root_data == None else key + root = Node(key, root_data) + root.is_root = True + obj.root_idx = 0 + obj.tree, obj.size = [root], 1 + obj.comparator = lambda key1, key2: key1 < key2 \ + if comp == None else comp + return obj + + def __str__(self): + return str([(node.left, node.key, node.data, node.right) + for node in self.tree]) + + +class BinarySearchTree(BinaryTree): + """ + Represents binary search trees. + + Examples + ======== + + >>> from pydatastructs.trees import BinarySearchTree as BST + >>> b = BST() + >>> b.insert(1, 1) + >>> b.insert(2, 2) + >>> child = b.tree[b.root_idx].right + >>> b.tree[child].data + 2 + >>> b.search(1) + 0 + >>> b.search(-1) == None + True + >>> b.delete(1) == True + True + >>> b.search(1) == None + True + >>> b.delete(2) == True + True + >>> b.search(2) == None + True + + References + ========== + + .. [1] https://en.wikipedia.org/wiki/Binary_search_tree + """ + def insert(self, key, data): + """ + Inserts data by the passed key using iterative + algorithm. + + Parameters + ========== + + key + The key for comparison. + data + The data to be inserted. + + Returns + ======= + + None + """ + walk = self.root_idx + if self.tree[walk].key == None: + self.tree[walk].key = key + self.tree[walk].data = data + return None + new_node = Node(key, data) + while True: + if self.tree[walk].key == key: + self.tree[walk].data = data + return None + if not self.comparator(key, self.tree[walk].key): + if self.tree[walk].right == None: + self.tree.append(new_node) + self.tree[walk].right = self.size + self.size += 1 + return None + walk = self.tree[walk].right + else: + if self.tree[walk].left == None: + self.tree.append(new_node) + self.tree[walk].left = self.size + self.size += 1 + return None + walk = self.tree[walk].left + + def search(self, key, **kwargs): + """ + Searches for the data in the binary search tree + using iterative algorithm. + + Parameters + ========== + + key + The key for searching. + parent: bool + If true then returns index of the + parent of the node with the passed + key. + By default, False + + Returns + ======= + + int + If the node with the passed key is + in the tree. + tuple + The index of the searched node and + the index of the parent of that node. + None + In all other cases. + """ + ret_parent = kwargs.get('parent', False) + parent = None + walk = self.root_idx + if self.tree[walk].key == None: + return None + while walk != None: + if self.tree[walk].key == key: + break + parent = walk + if self.comparator(key, self.tree[walk].key): + walk = self.tree[walk].left + else: + walk = self.tree[walk].right + return (walk, parent) if ret_parent else walk + + def delete(self, key): + """ + Deletes the data with the passed key + using iterative algorithm. + + Parameters + ========== + + key + The key of the node which is + to be deleted. + + Returns + ======= + + True + If the node is deleted successfully. + None + If the node to be deleted doesn't exists. + + Note + ==== + + The node is deleted means that the connection to that + node are removed but the it is still in three. This + is being done to keep the complexity of deletion, O(logn). + """ + (walk, parent) = self.search(key, parent=True) + if walk == None: + return None + if self.tree[walk].left == None and \ + self.tree[walk].right == None: + if parent == None: + self.tree[self.root_idx].data = None + self.tree[self.root_idx].key = None + else: + if self.tree[parent].left == walk: + self.tree[parent].left = None + else: + self.tree[parent].right = None + + elif self.tree[walk].left != None and \ + self.tree[walk].right != None: + twalk = self.tree[walk].right + par = walk + while self.tree[twalk].left != None: + par = twalk + twalk = self.tree[twalk].left + self.tree[walk].data = self.tree[twalk].data + self.tree[walk].key = self.tree[twalk].key + self.tree[par].left = self.tree[twalk].right + + else: + if self.tree[walk].left != None: + child = self.tree[walk].left + else: + child = self.tree[walk].right + if parent == None: + self.tree[self.root_idx].left = self.tree[child].left + self.tree[self.root_idx].right = self.tree[child].right + self.tree[self.root_idx].data = self.tree[child].data + self.tree[self.root_idx].key = self.tree[child].key + self.tree[child].left = None + self.tree[child].right = None + else: + if self.tree[parent].left == walk: + self.tree[parent].left = child + else: + self.tree[parent].right = child + + return True diff --git a/pydatastructs/trees/tests/__init__.py b/pydatastructs/trees/tests/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/pydatastructs/trees/tests/test_binary_trees.py b/pydatastructs/trees/tests/test_binary_trees.py new file mode 100644 index 000000000..ca64f6c3c --- /dev/null +++ b/pydatastructs/trees/tests/test_binary_trees.py @@ -0,0 +1,31 @@ +from pydatastructs.trees.binary_trees import BinarySearchTree +from pydatastructs.utils.raises_util import raises + +def test_BinarySearchTree(): + BST = BinarySearchTree + b = BST(8, 8) + b.insert(3, 3) + b.insert(10, 10) + b.insert(1, 1) + b.insert(6, 6) + b.insert(4, 4) + b.insert(7, 7) + b.insert(14, 14) + b.insert(13, 13) + assert str(b) == \ + ("[(1, 8, 8, 2), (3, 3, 3, 4), (None, 10, 10, 7), (None, 1, 1, None), " + "(5, 6, 6, 6), (None, 4, 4, None), (None, 7, 7, None), (8, 14, 14, None), " + "(None, 13, 13, None)]") + assert b.search(10) == 2 + assert b.search(-1) == None + assert b.delete(13) == True + assert b.search(13) == None + assert b.delete(10) == True + assert b.search(10) == None + assert b.delete(3) == True + assert b.search(3) == None + assert str(b) == \ + ("[(1, 8, 8, 7), (3, 4, 4, 4), (None, 10, 10, 7), (None, 1, 1, None), " + "(None, 6, 6, 6), (None, 4, 4, None), (None, 7, 7, None), (None, 14, 14, None), " + "(None, 13, 13, None)]") + raises(ValueError, lambda: BST(root_data=6))