Lecture Notes on 06 Nov 2020 * Two more traversals - depth first search (dfs) and breadth first search (bfs) * The algorithms for the two searchs are given below: Depth First Search (DFS) 1. Create a Stack 2. Add the root to the Stack 3. If the Stack is not empty pop the node from the Stack and make it the current node. 4. If the current node is not None print the data and add the right child and then the left child of the current node to the Stack. 5. Repeat steps 3 and 4 until the Stack is empty. Breadth First Search (BFS) 1. Create a Queue 2. Add the root to the Queue 3. If the Queue is not empty remove a node from the Queue and make it the current node. 4. If the current node is not None print the data and add the left child and then the right child of the current node to the Queue. 5. Repeat steps 3 and 4 until Queue is empty. * Deletion of a Node from a Binary Search Tree. Three cases to consider: 1. Leaf Node 2. Node with one child 3. Node with two children Case 1: Find out if the delete node is the left child or right child of its parent. Make the left child or the right child of the parent None. Case 2: Find out if the delete node is the left child or right child of its parent. Attach the child of the delete node to the left child or right child of the parent. Case 3: Find the successor node. The successor node is the node that comes immediately after the delete node during an inorder traversal. Think of the successor node as the minimum node in the right sub-tree of the delete node. The successor node will not have a left child but may have a right child. Replace the delete node with the successor node. Attach the left child of the delete node to the successor Attach the right child of the successor node as the left child of the successor's parent. Attach the right child of the delete node as the right child of the successor node. Here is the code: # delete a node with a given key def delete (self, key): delete_node = self.root parent = self.root is_left = False # if empty tree if (delete_node == None): return None # find the delete node while (delete_node != None) and (delete_node.data != key): parent = delete_node if (key < delete_node.data): delete_node = delete_node.lChild is_left = True else: delete_node = delete_node.rChild is_left = False # if node not found if (delete_node == None): return None # delete node is a leaf node if (delete_node.lChild == None) and (delete_node.rChild == None): if (delete_node == self.root): self.root = None elif (is_left): parent.lChild = None else: parent.rChild = None # delete node is a node with only left child elif (delete_node.rChild == None): if (delete_node == self.root): self.root = delete_node.lChild elif (is_left): parent.lChild = delete_node.lChild else: parent.rChild = delete_node.lChild # delete node is a node with only right child elif (delete_node.lChild == None): if (delete_node == self.root): self.root = delete_node.rChild elif (is_left): parent.lChild = delete_node.rChild else: parent.rChild = delete_node.rChild # delete node is a node with both left and right children else: # find delete node's successor and successor's parent nodes successor = delete_node.rChild successorParent = delete_node while (successor.lChild != None): successorParent = successor successor = successor.lChild # successor node is right child of delete node if (delete_node == self.root): self.root = successor elif (is_left): parent.lChild = successor else: parent.rChild = successor # connect delete node's left child to be successor's left child successor.lChild = delete_node.lChild # successor node left descendant of delete node if (successor != delete_node.rChild): successorParent.lChild = successor.rChild successor.rChild = delete_node.rChild return delete_node * Balance Factor = Height of left sub-tree - Height of right sub-tree * In a balanced tree the balance factor of every node is either -1, 0, 1 * AVL Trees https://www.cs.usfca.edu/~galles/visualization/AVLtree.html