Example #1
0
 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))
Example #9
0
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