def insert(self, data): """ Best case: when binary tree is a completely balanced tree O(log n) runtime complexity """ if self.root: self.root.insert(data) else: self.root = Node(data)
def test_bst_insert(self): arr = [item for item in self.bst_data] item = arr.pop(0) root = Node(item) while arr: item = arr.pop(0) node = Node(item) bst_insert(root, node) self.assertIsNotNone(root.left) self.assertIsNotNone(root.left.left) self.assertEqual(root.left.left.value, 43)
def test_rank(self): arr = [item for item in self.bst_data] item = arr.pop(0) root = Node(item) node = root while arr: item = arr.pop(0) parent = node node = Node(item) parent.left = node self.assertEqual(root.rank, 9) node.parent.parent.left = None self.assertEqual(root.rank, 7)
def insert(self, val): if self.val: # If no val empty tree, use base case if val < self.val: if self.left is None: self.left = Node(val) else: self.left.insert(val) else: if self.right is None: self.right = Node(val) else: self.right.insert(val) else: self.val = val
def test_level(self): arr = [item for item in self.bst_data] item = arr.pop(0) root = Node(item) node = root while arr: item = arr.pop(0) parent = node node = Node(item) parent.left = node self.assertEqual(node.level, 8) test_node = node.parent.parent test_node.parent = None self.assertEqual(test_node.level, 0) self.assertEqual(test_node.left.left.level, 2)
class BinarySearchTree: def __init__(self, root): pass def insert(self, val): if self.val: # If no val empty tree, use base case if val < self.val: if self.left is None: self.left = Node(val) else: self.left.insert(val) else: if self.right is None: self.right = Node(val) else: self.right.insert(val) else: self.val = val def print_tree(self): if self.left: self.left.print_tree() # base case - root node print(self.val) # printed the left most mode # now backtracking to previous node # when left nodes of current subtree are finished # print(root) # same for right subtree now if self.right: self.right.print_tree() # left -> root -> right def in_order_traversal(self, root): traversed_arr = [] # base case if(root): traversed_arr = self.in_order_traversal(root.left) traversed_arr.append(root.data) traversed_arr += self.in_order_traversal(root.right) return traversed_arr
def test_bst_delete_with_child(self): if __print__: print('--------------------test_bst_delete_with_child') bst = BST().from_list(self.bst_data) node = Node(82) bst_insert(bst.root, node) bst_delete(bst.root.left.left) if __print__: bst_print(bst.root) self.assertEqual(bst.root.left.rank, 2) self.assertEqual(bst.root.rank, 9) self.assertIsNotNone(bst.root.left.left) self.assertIsNone(bst.root.left.left.left) bst_delete(bst.root.right) if __print__: bst_print(bst.root) self.assertEqual(bst.root.rank, 8) if __print__: print('--------------------test_bst_delete_with_child')
self.left.print_tree() # base case - root node print(self.val) # printed the left most mode # now backtracking to previous node # when left nodes of current subtree are finished # print(root) # same for right subtree now if self.right: self.right.print_tree() # left -> root -> right def in_order_traversal(self, root): traversed_arr = [] # base case if(root): traversed_arr = self.in_order_traversal(root.left) traversed_arr.append(root.data) traversed_arr += self.in_order_traversal(root.right) return traversed_arr root = Node(27) bst = BinarySearchTree(root) bst.insert(Node(14)) bst.insert(Node(35)) bst.insert(Node(10)) bst.insert(Node(19)) bst.insert(Node(31)) bst.insert(Node(42)) # root.print_tree() # print(root.in_order_traversal(root))
class BST: def __init__(self): self.root = None # insert def insert(self, data): """ Best case: when binary tree is a completely balanced tree O(log n) runtime complexity """ if self.root: self.root.insert(data) else: self.root = Node(data) # def insert_node(data, current): # if data >= current.data: # # right # if not current.right: # current.right = Node(data) # else: # insert_node(data, current.right) # else: # # left # if not current.left: # current.left = Node(data) # else: # insert_node(data, current.left) # if not self.root: # self.root = Node(data) # else: # insert_node(data, self.root) # search def find(self, data): """ O(log n) worse case scenario """ if self.root: return self.root.search(data) else: return False # def search(data, current): # if data == current.data: # return current.data # # move left # if current and data < current.data: # return search(data, current.left) # # move right # elif current: # return search(data, current.right) # # if nothing is round # return None # return search(data, self.root) # delete def delete(self, data): """ O(log n) worse case scenario Case 1: node to be deleted is a leaf Case 2: node to be deleted has one child Case 3: node to be deleted has two children """ if not self.find(data): return f"{data} is not in the binary search tree." # get a reference of the parent node current = self.root parent = self.root is_left_child = False if not current: return while current and current.data != data: parent = current if data < current.data: current = current.left is_left_child = True else: current = current.right is_left_child = False if not current: return # Case 1 - is a leaf if not current.left and not current.right: if current == self.root: self.root == None else: if is_left_child: parent.left = None else: parent.right = None # Case 2 - has one child elif not current.right: if current == self.root: self.root = self.root.left elif is_left_child: parent.left = current.left else: parent.right = current.left elif not current.left: if current == self.root: self.root = self.root.right elif is_left_child: parent.left = current.right else: parent.right = current.right # Case 3 - has two children # find the node that is greater than the node to be deleted # but less than the greater node # find the successor node which is to the right of the node to be deleted but less than that node # bring the successor to the current node to be deleted, # make the left child of current node the left child of the successor node # then the right child of the current node is the right child of the successor node # then break the connection of the successor's parent # the current node's parent connection needs to connect to the current node's parent. # if the successor has a right child then the parent of the successor's left child becomes their left child... # in-order def in_order(self): """ Depth-first search 1. traverse the left sub-tree 2. visit the root 3. traverse the right sub-tree """ def order(current): if not current: return if current.left: return order(current.left) print(current.data, end=" ") if current.right: return order(current.right) order(self.root) # post-order def post_order(self): """ Depth-first search 1. traverse the left 2. traverse the right 3. visit the root """ def order(current): if not current: return if current.left: return order(current.left) if current.right: return order(current.right) print(current.data, end=" ") order(self.root) # pre-order def pre_order(self): """ Depth-first search 1. visit the root 2. traverse the left 3. traverse the right """ def order(current): if not current: return print(current.data, end=" ") if current.left: return order(current.left) if current.right: return order(current.right) order(self.root) # level-order def level_order(self): """ Breadth-first search """ current = self.root if not current: return q = [current] while q: current = q.pop(0) print(current.data, end=" ") if current.left: q.append(current.left) if current.right: q.append(current.right) # height def height(self): pass def smallest_value(self): current = self.root if current: while True: if not current.left: return current.data current = current.left def largest_value(self): current = self.root if current: while True: if not current.right: return current.data current = current.right