Binary Search Trees What is a Binary Search
Binary Search Trees
What is a Binary Search Tree? • A binary tree where each node is an object • Each node has a key value, left child, and right child (might be empty) • Each node satisfies the binary search tree property • Let x be a node in the BST. The left child’s key must be <= x’s key. The right child’s key must be >= x’s key
Implementing Binary Trees • We can use arrays or linked structures to implement binary trees • If using an array, each element stores a structure that has an information field and two “pointer” fields containing the indexes of the array locations of the left and right children • The root of the tree is always in the first cell of the array, and a value of -1 indicates an empty child 3
Implementing Binary Trees (continued) • Implementing binary tree arrays does have drawbacks • We need to keep track of the locations of each node, and these have to be located sequentially • Deletions are also awkward, requiring tags to mark empty cells, or moving elements around, requiring updating values • Consequently, while arrays are convenient, we’ll usually use a linked implementation • In a linked implementation, the node is defined by a class, and consists of an information data member and two pointer data members • The node is manipulated by methods defined in another class that represents the tree 4
Searching a BST Runs in O(h) time but this could be O(n) in the worst case! O(lgn) if the tree is balanced! Finding min and max?
Tree Traversal • Tree traversal is the process of visiting each node in a tree data structure exactly one time • This definition only specifies that each node is visited, but does not indicate the order of the process • Hence, there are numerous possible traversals; in a tree of n nodes there are n! traversals • Two especially useful traversals are depth-first traversals and breadth-first traversals 6
Tree Traversal (continued) • Breadth-First Traversal • Breadth-first traversal proceeds level-by-level from top-down generally visiting each level’s nodes left-to-right • This can be easily implemented using a queue • If we consider a top-down, left-to-right breadth-first traversal, we start by placing the root node in the queue • We then remove the node at the front of the queue, and after visiting it, we place its children (if any) in the queue • This is repeated until the queue is empty 7
Breadth-First Traversal (continued) • The following diagram shows a traversal of the tree from Figure 6. 6 c, using the queue-based breadth-first traversal 8
Tree Traversal (continued) • Breadth-First Traversal (continued) • An implementation of this is shown below 9
Depth-First Traversal • Depth-first traversal proceeds by following left- (or right-) hand branches as far as possible • The algorithm then backtracks to the most recent fork and takes the right- (or left-) hand branch to the next node • It then follows branches to the left (or right) again as far as possible • This process continues until all nodes have been visited • While this process is straightforward, it doesn’t indicate at what point the nodes are visited; there are variations that can be used • We are interested in three activities: traversing to the left, traversing to the right, and visiting a node • These activities are labeled L, R, and V, for ease of representation 10
Depth-First Traversal (continued) • Based on earlier discussions, we want to perform the traversal in an orderly manner, so there are six possible arrangements: • VLR, VRL, LVR, LRV, RVL, and RLV • Generally, we follow the convention of traversing from left to right, which narrows this down to three traversals: • VLR – known as preorder traversal • LVR – known as inorder traversal • LRV – known as postorder traversal • These can be implemented with a small amount of code! Data Structures and Algorithms in C++, Fourth Edition 11
Depth First Search Implementations 12
Depth First Search Implementations 13
Depth First Search Implementations 14
Depth-First Traversal (continued) • While the code is simple, the power lies in the recursion supported by the run-time stack, which can place a heavy burden on the system • A non-recursive implementation of the traversal algorithms is possible but we’d generally have to manage our own stack • It is also possible to incorporate the “stack” into the design of the tree itself • Done using threads, pointers to the predecessor and successor of a node based on an inorder traversal • Trees with threads are called threaded trees 15
Stackless Depth-First Traversal: Threaded Trees (continued) (a) A threaded tree and (b) an inorder traversal’s path in a threaded tree with right successors only 16
Successor • Finding the node with the next largest (or equal) value O(h) runtime
Insertion • Searching a binary tree does not modify the tree • Traversals may temporarily modify the tree, but it is usually left in its original form when the traversal is done • Operations like insertions, deletions, modifying values, merging trees, and balancing trees do alter the tree structure • We’ll look at how insertions are managed in binary search trees first • In order to insert a new node in a binary tree, we have to be at a node with a vacant left or right child • This is performed in the same way as searching: • Compare the value of the node to be inserted to the current node • If the value to be inserted is smaller, follow the left subtree; if it is larger, follow the right subtree • If the branch we are to follow is empty, we stop the search and insert the new node as that child 18
Insertion (continued) 19
Insertion O(h) runtime
Deletion • Deleting a node z from a BST T 1. If z has no children the simply remove it by modifying its parent to replace z with nil as its child 2. If z has just one child then we elevate that child to take z’s position in the tree by modifying z’s parent to replace z by z’s child 3. If z has two children then: (deletion by copying) • Find z’s successor y – which must be in z’s right subtree – and have y take z’s position in the tree • As a successor y in the right subtree, y has at most one child. Remove y using rule 2 • The rest of z’s original right subtree becomes y’s right subtree and z’s left subtree becomes y’s left subtree
Delete Examples
Deletion Delete node z First two cases handled by: If left child is nil, transplant with right child If right child is nil, transplant with left child
Delete with two children
Transplant • Helper function for deletion • Node v is a child of node u (v could be nil) • Replaces u with v and updates parent of u to have v as left or right child • Handles first two cases; partially handles third case but need to update children of v u v
Deletion Tree-Delete(T, z) if z. left == NIL Transplant(T, z, z. right) else if z. right == NIL Transplant(T, z, z. left) else y = Tree-Minimum(z. right) if y. p != z Transplant(T, y, y, right) y. right = z. right y. right. p = y Transplant(T, z, y) y. left = z. left y. left. p = y
BST • Worst case? • Best case? • Expectation for randomly built BST?
- Slides: 27