1 C++ Classes and Data Structures Jeffrey S. Childs Chapter 15 Other Data Structures Jeffrey S. Childs Clarion University of PA © 2008, Prentice Hall
2 Binary Search Trees A binary search tree is a binary tree that allows us to search for values that can be anywhere in the tree Usually, we search for a certain key value, and once we find the node that contains it, we retrieve the rest of the info at that node Therefore, we assume that all values searched for in a binary search tree are distinct
3 Properties of Binary Search Trees A binary search tree does not have to be a complete binary tree (like a heap) For any particular node, –the key in its left child (if any) is less than its key –the key in its right child (if any) is greater than its key
4 Binary Search Tree Node template BSTNode { DataType info; BSTNode *left; BSTNode *right; }; The implementation of a binary search tree usually just maintains a single pointer in the private section called root, to point to the root node.
5 Inserting Nodes Into a BST 37, 2, 45, 48, 41, 29, 20, 30, 49, 7 Objects that need to be inserted (only key values are shown): root: NULL BST starts off empty
6 Inserting Nodes Into a BST (cont.) 37, 2, 45, 48, 41, 29, 20, 30, 49, 7 root 37
7 Inserting Nodes Into a BST (cont.) 2, 45, 48, 41, 29, 20, 30, 49, 7 root 37
8 Inserting Nodes Into a BST (cont.) 2, 45, 48, 41, 29, 20, 30, 49, 7 root 37
9 Inserting Nodes Into a BST (cont.) 2, 45, 48, 41, 29, 20, 30, 49, 7 root 37 2 < 37, so insert 2 on the left side of 37
10 Inserting Nodes Into a BST (cont.) 2, 45, 48, 41, 29, 20, 30, 49, 7 root 37 2
11 Inserting Nodes Into a BST (cont.) 45, 48, 41, 29, 20, 30, 49, 7 root 37 2
12 Inserting Nodes Into a BST (cont.) 45, 48, 41, 29, 20, 30, 49, 7 root 37 2
13 Inserting Nodes Into a BST (cont.) 45, 48, 41, 29, 20, 30, 49, 7 root > 37, so insert it at the right of 37
14 Inserting Nodes Into a BST (cont.) 45, 48, 41, 29, 20, 30, 49, 7 root
15 Inserting Nodes Into a BST (cont.) 48, 41, 29, 20, 30, 49, 7 root
16 Inserting Nodes Into a BST (cont.) 48, 41, 29, 20, 30, 49, 7 root
17 Inserting Nodes Into a BST (cont.) 48, 41, 29, 20, 30, 49, 7 root When comparing, we always start at the root node
18 Inserting Nodes Into a BST (cont.) 48, 41, 29, 20, 30, 49, 7 root > 37, so look to the right
19 Inserting Nodes Into a BST (cont.) 48, 41, 29, 20, 30, 49, 7 root > 37, so look to the right
20 Inserting Nodes Into a BST (cont.) 48, 41, 29, 20, 30, 49, 7 root This time, there is a node already to the right of the root node. We then compare 48 to this node
21 Inserting Nodes Into a BST (cont.) 48, 41, 29, 20, 30, 49, 7 root > 45, and 45 has no right child, so we insert 48 on the right of 45
22 Inserting Nodes Into a BST (cont.) 48, 41, 29, 20, 30, 49, 7 root
23 Inserting Nodes Into a BST (cont.) 41, 29, 20, 30, 49, 7 root
24 Inserting Nodes Into a BST (cont.) 41, 29, 20, 30, 49, 7 root
25 Inserting Nodes Into a BST (cont.) 41, 29, 20, 30, 49, 7 root > 37, so look to the right
26 Inserting Nodes Into a BST (cont.) 41, 29, 20, 30, 49, 7 root
27 Inserting Nodes Into a BST (cont.) 41, 29, 20, 30, 49, 7 root < 45, so look to the left – there is no left child, so insert
28 Inserting Nodes Into a BST (cont.) 41, 29, 20, 30, 49, 7 root
29 Inserting Nodes Into a BST (cont.) 29, 20, 30, 49, 7 root
30 Inserting Nodes Into a BST (cont.) 29, 20, 30, 49, 7 root
31 Inserting Nodes Into a BST (cont.) 29, 20, 30, 49, 7 root < 37
32 Inserting Nodes Into a BST (cont.) 29, 20, 30, 49, 7 root
33 Inserting Nodes Into a BST (cont.) 29, 20, 30, 49, 7 root > 2
34 Inserting Nodes Into a BST (cont.) 29, 20, 30, 49, 7 root
35 Inserting Nodes Into a BST (cont.) 20, 30, 49, 7 root
36 Inserting Nodes Into a BST (cont.) 20, 30, 49, 7 root
37 Inserting Nodes Into a BST (cont.) 20, 30, 49, 7 root < 37
38 Inserting Nodes Into a BST (cont.) 20, 30, 49, 7 root
39 Inserting Nodes Into a BST (cont.) 20, 30, 49, 7 root > 2
40 Inserting Nodes Into a BST (cont.) 20, 30, 49, 7 root
41 Inserting Nodes Into a BST (cont.) 20, 30, 49, 7 root < 29
42 Inserting Nodes Into a BST (cont.) 20, 30, 49, 7 root
43 Inserting Nodes Into a BST (cont.) 30, 49, 7 root
44 Inserting Nodes Into a BST (cont.) 30, 49, 7 root
45 Inserting Nodes Into a BST (cont.) 30, 49, 7 root < 37
46 Inserting Nodes Into a BST (cont.) 30, 49, 7 root
47 Inserting Nodes Into a BST (cont.) 30, 49, 7 root > 2
48 Inserting Nodes Into a BST (cont.) 30, 49, 7 root
49 Inserting Nodes Into a BST (cont.) 30, 49, 7 root > 29
50 Inserting Nodes Into a BST (cont.) 30, 49, 7 root
51 Inserting Nodes Into a BST (cont.) 49, 7 root
52 Inserting Nodes Into a BST (cont.) 49, 7 root
53 Inserting Nodes Into a BST (cont.) 49, 7 root > 37
54 Inserting Nodes Into a BST (cont.) 49, 7 root
55 Inserting Nodes Into a BST (cont.) 49, 7 root > 45
56 Inserting Nodes Into a BST (cont.) 49, 7 root
57 Inserting Nodes Into a BST (cont.) 49, 7 root > 48
58 Inserting Nodes Into a BST (cont.) 49, 7 root
59 Inserting Nodes Into a BST (cont.) 7 root
60 Inserting Nodes Into a BST (cont.) 7 root
61 Inserting Nodes Into a BST (cont.) 7 root < 37
62 Inserting Nodes Into a BST (cont.) 7 root
63 Inserting Nodes Into a BST (cont.) 7 root > 2
64 Inserting Nodes Into a BST (cont.) 7 root
65 Inserting Nodes Into a BST (cont.) 7 root < 29
66 Inserting Nodes Into a BST (cont.) 7 root
67 Inserting Nodes Into a BST (cont.) 7 root < 20
68 Inserting Nodes Into a BST (cont.) 7 root
69 Inserting Nodes Into a BST (cont.) root
70 Inserting Nodes Into a BST (cont.) root All elements have been inserted 7
71 Searching for a Key in a BST root Searching for a key in a BST uses the same logic 7
72 Searching for a Key in a BST (cont.) root Key to search for: 29 7
73 Searching for a Key in a BST (cont.) root Key to search for: 29 7
74 Searching for a Key in a BST (cont.) root Key to search for: < 37 7
75 Searching for a Key in a BST (cont.) root Key to search for: 29 7
76 Searching for a Key in a BST (cont.) root Key to search for: > 2 7
77 Searching for a Key in a BST (cont.) root Key to search for: 29 7
78 Searching for a Key in a BST (cont.) root Key to search for: == 29 FOUND IT! 7
79 Searching for a Key in a BST (cont.) root
80 Searching for a Key in a BST (cont.) root Key to search for: 3 7
81 Searching for a Key in a BST (cont.) root Key to search for: 3 7
82 Searching for a Key in a BST (cont.) root Key to search for: 3 3 < 37 7
83 Searching for a Key in a BST (cont.) root Key to search for: 3 7
84 Searching for a Key in a BST (cont.) root Key to search for: 3 3 > 2 7
85 Searching for a Key in a BST (cont.) root Key to search for: 3 7
86 Searching for a Key in a BST (cont.) root Key to search for: 3 3 < 29 7
87 Searching for a Key in a BST (cont.) root Key to search for: 3 7
88 Searching for a Key in a BST (cont.) root Key to search for: 3 3 < 20 7
89 Searching for a Key in a BST (cont.) root Key to search for: 3 7
90 Searching for a Key in a BST (cont.) root Key to search for: 3 3 < 7 7
91 Searching for a Key in a BST (cont.) root Key to search for: 3 When the child pointer you want to follow is set to NULL, the key you are looking for is not in the BST 7
92 Time Complexities If the binary search tree happens to be a complete binary tree: –the time for insertion is ( lg n ) –the time for the search is O( lg n ) –similar to swapping upwards or downwards through the heap However, we could run into some bad luck…
93 Bad Luck 2, 7, 20, 29, 30, 37, 41, 45, 48, 49 root Exactly the same keys were inserted into this BST – but they were inserted in a different order (the order shown below)
94 Bad Luck (cont.) 2, 7, 20, 29, 30, 37, 41, 45, 48, 49 root This is some bad luck, but a BST can be formed this way
95 Bad Luck (cont.) 2, 7, 20, 29, 30, 37, 41, 45, 48, 49 root Using the “tightest” possible big-oh notation, the insertion and search time is O( n )
96 Balanced vs. Unbalanced If a BST takes ( lg n ) time for insertion, and O( lg n ) time for a search, we say it is a balanced binary search tree If a BST take O( n ) time for insertion and searching, we say it is an unbalanced binary search tree These definitions assume the tightest possible big-oh notation
97 Deleting a BST Node Deleting a node in a BST is a little tricky – it has to be deleted so that the resulting structure is still a BST with each node greater than its left child and less than its right child Deleting a node is handled differently depending on whether the node: –has no children –has one child –has two children
98 Deletion Case 1: No Children root Node 49 has no children – to delete it, we just remove it
99 Deletion Case 1: No Children (cont.) root
100 Deletion Case 2: One Child root Node 48 has one child – to delete it, we just splice it out
101 Deletion Case 2: One Child (cont.) root Node 48 has one child – to delete it, we just splice it out
102 Deletion Case 2: One Child (cont.) root
103 Deletion Case 2: One Child (cont.) root Another example: node 2 has one child – to delete it we also splice it out
104 Deletion Case 2: One Child (cont.) root Another example: node 2 has one child – to delete it we also splice it out
105 Deletion Case 2: One Child (cont.) root
106 Deletion Case 3: Two Children root Node 37 has two children…
107 Deletion Case 3: Two Children (cont.) root to delete it, first we find the greatest node in its left subtree
108 Deletion Case 3: Two Children (cont.) root First, we go to the left once, then follow the right pointers as far as we can to delete it, first we find the greatest node in its left subtree
109 Deletion Case 3: Two Children (cont.) root to delete it, first we find the greatest node in its left subtree First, we go to the left once, then follow the right pointers as far as we can
110 Deletion Case 3: Two Children (cont.) root to delete it, first we find the greatest node in its left subtree First, we go to the left once, then follow the right pointers as far as we can
111 Deletion Case 3: Two Children (cont.) root to delete it, first we find the greatest node in its left subtree First, we go to the left once, then follow the right pointers as far as we can
112 Deletion Case 3: Two Children (cont.) root is the greatest node in the left subtree of node 37 First, we go to the left once, then follow the right pointers as far as we can
113 Deletion Case 3: Two Children (cont.) root Next, we copy the object at node 30 into node 37
114 Deletion Case 3: Two Children (cont.) root Next, we copy the object at node 30 into node 37
115 Deletion Case 3: Two Children (cont.) root Next, we copy the object at node 30 into node 37
116 Deletion Case 3: Two Children (cont.) root Finally, we delete the lower red node using case 1 or case 2 deletion
117 Deletion Case 3: Two Children (cont.) root
118 Deletion Case 3: Two Children (cont.) root Let’s delete node 30 now
119 Deletion Case 3: Two Children (cont.) root
120 Deletion Case 3: Two Children (cont.) root
121 Deletion Case 3: Two Children (cont.) root The greatest node in the left subtree of node 30
122 Deletion Case 3: Two Children (cont.) root The greatest node in the left subtree of node 30
123 Deletion Case 3: Two Children (cont.) root The greatest node in the left subtree of node 30
124 Deletion Case 3: Two Children (cont.) root This time, the lower red node has a child – to delete it we use case 2 deletion
125 Deletion Case 3: Two Children (cont.) root This time, the lower red node has a child – to delete it we use case 2 deletion
126 Deletion Case 3: Two Children (cont.) root
127 Deletion Time Complexity In all cases, we must find the node we wish to delete first, using the standard search method. Finding the greatest node in the left subtree is just a continuation of a path down the BST For balanced BST’s, the time complexity for deletion is O( lg n ) in all 3 cases For unbalanced BST’s, the time complexity is O( n ) in all 3 cases
128 Why Use BST’s? Hash tables can search in ( 1 ) time, so why use BST’s? BST’s have the ability to provide all keys in order, in ( n ) time To do so, it uses a recursive function called InOrder2, and a driver for the recursive function called InOrder…
129 1 template 2 bool BinarySearchTree::InOrder( Array & arr ) 3 { 4if ( size == 0 ) 5return false; 6arr.changeSize( size ); 7int i = 0; 8InOrder2( root, arr, i ); 9return true; 10 } The client uses the driver called InOrder, passing in an Array by reference, which will contain the objects in order by key after returning from InOrder Getting Elements In Order
130 1 template 2 bool BinarySearchTree::InOrder( Array & arr ) 3 { 4if ( size == 0 ) 5return false; 6arr.changeSize( size ); 7int i = 0; 8InOrder2( root, arr, i ); 9return true; 10 } If the BST has no elements, false is returned Getting Elements In Order (cont.)
131 1 template 2 bool BinarySearchTree::InOrder( Array & arr ) 3 { 4if ( size == 0 ) 5return false; 6arr.changeSize( size ); 7int i = 0; 8InOrder2( root, arr, i ); 9return true; 10 } The size of the Array is changed, so that it contains enough elements for all the nodes in the BST – the client may have set the size of the Array correctly, but we like to play it safe Getting Elements In Order (cont.)
132 1 template 2 bool BinarySearchTree::InOrder( Array & arr ) 3 { 4if ( size == 0 ) 5return false; 6arr.changeSize( size ); 7int i = 0; 8InOrder2( root, arr, i ); 9return true; 10 } i is used to index the Array Getting Elements In Order (cont.)
133 1 template 2 bool BinarySearchTree::InOrder( Array & arr ) 3 { 4if ( size == 0 ) 5return false; 6arr.changeSize( size ); 7int i = 0; 8InOrder2( root, arr, i ); 9return true; 10 } The recursive function InOrder2 is called, passing arr by reference – when the top-level InOrder2 returns, arr will contain the objects with keys in sorted order Getting Elements In Order (cont.)
134 1 template 2 bool BinarySearchTree::InOrder( Array & arr ) 3 { 4if ( size == 0 ) 5return false; 6arr.changeSize( size ); 7int i = 0; 8InOrder2( root, arr, i ); 9return true; 10 } So let’s now focus on the InOrder2 function… Getting Elements In Order (cont.)
135 1 template 2 void BinarySearchTree::InOrder2( 3 BSTNode *ptr, Array & arr, int & i ) 4 { 5if ( ptr != NULL ) { 6InOrder2( ptr->left, arr, i ); 7arr[ i ] = ptr->info; 8i++; 9InOrder2( ptr->right, arr, i ); 10} 11 } Getting Elements In Order (cont.)
136 1 template 2 void BinarySearchTree::InOrder2( 3 BSTNode *ptr, Array & arr, int & i ) 4 { 5if ( ptr != NULL ) { 6InOrder2( ptr->left, arr, i ); 7arr[ i ] = ptr->info; 8i++; 9InOrder2( ptr->right, arr, i ); 10} 11 } This is the only line which places info in the array… Getting Elements In Order (cont.)
137 1 template 2 void BinarySearchTree::InOrder2( 3 BSTNode *ptr, Array & arr, int & i ) 4 { 5if ( ptr != NULL ) { 6InOrder2( ptr->left, arr, i ); 7arr[ i ] = ptr->info; 8i++; 9InOrder2( ptr->right, arr, i ); 10} 11 } then i is incremented Getting Elements In Order (cont.)
138 1 template 2 void BinarySearchTree::InOrder2( 3 BSTNode *ptr, Array & arr, int & i ) 4 { 5if ( ptr != NULL ) { 6InOrder2( ptr->left, arr, i ); 7arr[ i ] = ptr->info; 8i++; 9InOrder2( ptr->right, arr, i ); 10} 11 } i is passed by reference, so every time it is incremented, the calling function knows about it Getting Elements In Order (cont.)
139 1 template 2 void BinarySearchTree::InOrder2( 3 BSTNode *ptr, Array & arr, int & i ) 4 { 5if ( ptr != NULL ) { 6InOrder2( ptr->left, arr, i ); 7arr[ i ] = ptr->info; 8i++; 9InOrder2( ptr->right, arr, i ); 10} 11 } The base case occurs when ptr is NULL – just returns Getting Elements In Order (cont.)
140 1 template 2 void BinarySearchTree::InOrder2( 3 BSTNode *ptr, Array & arr, int & i ) 4 { 5if ( ptr != NULL ) { 6InOrder2( ptr->left, arr, i ); 7arr[ i ] = ptr->info; 8i++; 9InOrder2( ptr->right, arr, i ); 10} 11 } There are two recursive calls under the if – the first advances the pointer to the left child… Getting Elements In Order (cont.)
141 1 template 2 void BinarySearchTree::InOrder2( 3 BSTNode *ptr, Array & arr, int & i ) 4 { 5if ( ptr != NULL ) { 6InOrder2( ptr->left, arr, i ); 7arr[ i ] = ptr->info; 8i++; 9InOrder2( ptr->right, arr, i ); 10} 11 } and the second advances the pointer to the right child Getting Elements In Order (cont.)
142 1 template 2 void BinarySearchTree::InOrder2( 3 BSTNode *ptr, Array & arr, int & i ) 4 { 5if ( ptr != NULL ) { 6InOrder2( ptr->left, arr, i ); 7arr[ i ] = ptr->info; 8i++; 9InOrder2( ptr->right, arr, i ); 10} 11 } Notice that this will be the same ptr in both cases Getting Elements In Order (cont.)
143 1 template 2 void BinarySearchTree::InOrder2( 3 BSTNode *ptr, Array & arr, int & i ) 4 { 5if ( ptr != NULL ) { 6InOrder2( ptr->left, arr, i ); 7arr[ i ] = ptr->info; 8i++; 9InOrder2( ptr->right, arr, i ); 10} 11 } Notice that each recursive call approaches the base case… Getting Elements In Order (cont.)
144 1 template 2 void BinarySearchTree::InOrder2( 3 BSTNode *ptr, Array & arr, int & i ) 4 { 5if ( ptr != NULL ) { 6InOrder2( ptr->left, arr, i ); 7arr[ i ] = ptr->info; 8i++; 9InOrder2( ptr->right, arr, i ); 10} 11 } it goes down one level through the tree, and so it must get closer to the case where ptr->left or ptr- >right is NULL Getting Elements In Order (cont.)
145 1 template 2 void BinarySearchTree::InOrder2( 3 BSTNode *ptr, Array & arr, int & i ) 4 { 5if ( ptr != NULL ) { 6InOrder2( ptr->left, arr, i ); 7arr[ i ] = ptr->info; 8i++; 9InOrder2( ptr->right, arr, i ); 10} 11 } If the BST contains only one node, does InOrder2 work? Getting Elements In Order (cont.)
146 1 template 2 void BinarySearchTree::InOrder2( 3 BSTNode *ptr, Array & arr, int & i ) 4 { 5if ( ptr != NULL ) { 6InOrder2( ptr->left, arr, i ); 7arr[ i ] = ptr->info; 8i++; 9InOrder2( ptr->right, arr, i ); 10} 11 } In this case, the root node is the only node – the left and right pointers of the root node will be set to NULL. Getting Elements In Order (cont.)
147 1 template 2 void BinarySearchTree::InOrder2( 3 BSTNode *ptr, Array & arr, int & i ) 4 { 5if ( ptr != NULL ) { 6InOrder2( ptr->left, arr, i ); 7arr[ i ] = ptr->info; 8i++; 9InOrder2( ptr->right, arr, i ); 10} 11 } Getting Elements In Order (cont.)
148 1 template 2 void BinarySearchTree::InOrder2( 3 BSTNode *ptr, Array & arr, int & i ) 4 { 5if ( ptr != NULL ) { 6InOrder2( ptr->left, arr, i ); 7arr[ i ] = ptr->info; 8i++; 9InOrder2( ptr->right, arr, i ); 10} 11 } ptr->left is NULL, so NULL is passed into ptr of the new InOrder2 function that is made Getting Elements In Order (cont.)
149 1 template 2 void BinarySearchTree::InOrder2( 3 BSTNode *ptr, Array & arr, int & i ) 4 { 5if ( ptr != NULL ) { 6InOrder2( ptr->left, arr, i ); 7arr[ i ] = ptr->info; 8i++; 9InOrder2( ptr->right, arr, i ); 10} 11 } Since ptr is NULL in the new InOrder2 function, it will return right away Getting Elements In Order (cont.)
150 1 template 2 void BinarySearchTree::InOrder2( 3 BSTNode *ptr, Array & arr, int & i ) 4 { 5if ( ptr != NULL ) { 6InOrder2( ptr->left, arr, i ); 7arr[ i ] = ptr->info; 8i++; 9InOrder2( ptr->right, arr, i ); 10} 11 } Getting Elements In Order (cont.)
151 1 template 2 void BinarySearchTree::InOrder2( 3 BSTNode *ptr, Array & arr, int & i ) 4 { 5if ( ptr != NULL ) { 6InOrder2( ptr->left, arr, i ); 7arr[ i ] = ptr->info; 8i++; 9InOrder2( ptr->right, arr, i ); 10} 11 } i is still 0 from the driver – ptr is the root at this level – so the info at the root is placed into arr[ 0 ] Getting Elements In Order (cont.)
152 1 template 2 void BinarySearchTree::InOrder2( 3 BSTNode *ptr, Array & arr, int & i ) 4 { 5if ( ptr != NULL ) { 6InOrder2( ptr->left, arr, i ); 7arr[ i ] = ptr->info; 8i++; 9InOrder2( ptr->right, arr, i ); 10} 11 } i becomes 1 Getting Elements In Order (cont.)
153 1 template 2 void BinarySearchTree::InOrder2( 3 BSTNode *ptr, Array & arr, int & i ) 4 { 5if ( ptr != NULL ) { 6InOrder2( ptr->left, arr, i ); 7arr[ i ] = ptr->info; 8i++; 9InOrder2( ptr->right, arr, i ); 10} 11 } ptr->right is NULL – thus, the recursive function call immediately returns as before Getting Elements In Order (cont.)
154 1 template 2 void BinarySearchTree::InOrder2( 3 BSTNode *ptr, Array & arr, int & i ) 4 { 5if ( ptr != NULL ) { 6InOrder2( ptr->left, arr, i ); 7arr[ i ] = ptr->info; 8i++; 9InOrder2( ptr->right, arr, i ); 10} 11 } InOrder2 returns back to the driver with the correct array Getting Elements In Order (cont.)
155 1 template 2 void BinarySearchTree::InOrder2( 3 BSTNode *ptr, Array & arr, int & i ) 4 { 5if ( ptr != NULL ) { 6InOrder2( ptr->left, arr, i ); 7arr[ i ] = ptr->info; 8i++; 9InOrder2( ptr->right, arr, i ); 10} 11 } InOrder2 works correctly when there is only one node Getting Elements In Order (cont.)
156 1 template 2 void BinarySearchTree::InOrder2( 3 BSTNode *ptr, Array & arr, int & i ) 4 { 5if ( ptr != NULL ) { 6InOrder2( ptr->left, arr, i ); 7arr[ i ] = ptr->info; 8i++; 9InOrder2( ptr->right, arr, i ); 10} 11 } So InOrder2 works correctly when there is only one node in the BST or the BST is empty (root is NULL) Getting Elements In Order (cont.)
157 1 template 2 void BinarySearchTree::InOrder2( 3 BSTNode *ptr, Array & arr, int & i ) 4 { 5if ( ptr != NULL ) { 6InOrder2( ptr->left, arr, i ); 7arr[ i ] = ptr->info; 8i++; 9InOrder2( ptr->right, arr, i ); 10} 11 } Thus, it will work for a larger tree with 0 or 1 nodes in the left subtree, and 0 or 1 nodes in the right subtree, etc., etc. Getting Elements In Order (cont.)
158 1 template 2 void BinarySearchTree::InOrder2( 3 BSTNode *ptr, Array & arr, int & i ) 4 { 5if ( ptr != NULL ) { 6InOrder2( ptr->left, arr, i ); 7arr[ i ] = ptr->info; 8i++; 9InOrder2( ptr->right, arr, i ); 10} 11 } Consider the general case: p nodes in the left subtree and r nodes in the right subtree Getting Elements In Order (cont.)
159 1 template 2 void BinarySearchTree::InOrder2( 3 BSTNode *ptr, Array & arr, int & i ) 4 { 5if ( ptr != NULL ) { 6InOrder2( ptr->left, arr, i ); 7arr[ i ] = ptr->info; 8i++; 9InOrder2( ptr->right, arr, i ); 10} 11 } We assume that InOrder2 does what it is supposed to do Getting Elements In Order (cont.)
160 1 template 2 void BinarySearchTree::InOrder2( 3 BSTNode *ptr, Array & arr, int & i ) 4 { 5if ( ptr != NULL ) { 6InOrder2( ptr->left, arr, i ); 7arr[ i ] = ptr->info; 8i++; 9InOrder2( ptr->right, arr, i ); 10} 11 } The p nodes in the left subtree are all less than the root node, and will occupy the first p positions of the array. Getting Elements In Order (cont.)
161 1 template 2 void BinarySearchTree::InOrder2( 3 BSTNode *ptr, Array & arr, int & i ) 4 { 5if ( ptr != NULL ) { 6InOrder2( ptr->left, arr, i ); 7arr[ i ] = ptr->info; 8i++; 9InOrder2( ptr->right, arr, i ); 10} 11 } i is always incremented after adding an element, so… Getting Elements In Order (cont.)
162 1 template 2 void BinarySearchTree::InOrder2( 3 BSTNode *ptr, Array & arr, int & i ) 4 { 5if ( ptr != NULL ) { 6InOrder2( ptr->left, arr, i ); 7arr[ i ] = ptr->info; 8i++; 9InOrder2( ptr->right, arr, i ); 10} 11 } The root writes its info correctly in position p + 1 of the array Getting Elements In Order (cont.)
163 1 template 2 void BinarySearchTree::InOrder2( 3 BSTNode *ptr, Array & arr, int & i ) 4 { 5if ( ptr != NULL ) { 6InOrder2( ptr->left, arr, i ); 7arr[ i ] = ptr->info; 8i++; 9InOrder2( ptr->right, arr, i ); 10} 11 } Then, the last r nodes of the right subtree (all greater than the root) are written in the last r positions of the array Getting Elements In Order (cont.)
164 1 template 2 void BinarySearchTree::InOrder2( 3 BSTNode *ptr, Array & arr, int & i ) 4 { 5if ( ptr != NULL ) { 6InOrder2( ptr->left, arr, i ); 7arr[ i ] = ptr->info; 8i++; 9InOrder2( ptr->right, arr, i ); 10} 11 } Again, we assume InOrder2 does what it is supposed to do Getting Elements In Order (cont.)
165 1 template 2 void BinarySearchTree::InOrder2( 3 BSTNode *ptr, Array & arr, int & i ) 4 { 5if ( ptr != NULL ) { 6InOrder2( ptr->left, arr, i ); 7arr[ i ] = ptr->info; 8i++; 9InOrder2( ptr->right, arr, i ); 10} 11 } Thus, if we assume the recursive call of InOrder2 does what it is supposed to do, we have established the correctness… Getting Elements In Order (cont.)
166 1 template 2 void BinarySearchTree::InOrder2( 3 BSTNode *ptr, Array & arr, int & i ) 4 { 5if ( ptr != NULL ) { 6InOrder2( ptr->left, arr, i ); 7arr[ i ] = ptr->info; 8i++; 9InOrder2( ptr->right, arr, i ); 10} 11 } since InOrder2 works with smaller cases, we build larger cases from the smaller cases, using the smaller cases as left and right subtrees Getting Elements In Order (cont.)
167 1 template 2 void BinarySearchTree::InOrder2( 3 BSTNode *ptr, Array & arr, int & i ) 4 { 5if ( ptr != NULL ) { 6InOrder2( ptr->left, arr, i ); 7arr[ i ] = ptr->info; 8i++; 9InOrder2( ptr->right, arr, i ); 10} 11 } How do we know the time complexity is ( n )? Getting Elements In Order (cont.)
168 1 template 2 void BinarySearchTree::InOrder2( 3 BSTNode *ptr, Array & arr, int & i ) 4 { 5if ( ptr != NULL ) { 6InOrder2( ptr->left, arr, i ); 7arr[ i ] = ptr->info; 8i++; 9InOrder2( ptr->right, arr, i ); 10} 11 } InOrder2 is called once from the driver. Then, for each node in the tree, 2 calls to InOrder2 are made, giving us a total of 2n + 1 calls. Getting Elements In Order (cont.)
169 1 template 2 void BinarySearchTree::InOrder2( 3 BSTNode *ptr, Array & arr, int & i ) 4 { 5if ( ptr != NULL ) { 6InOrder2( ptr->left, arr, i ); 7arr[ i ] = ptr->info; 8i++; 9InOrder2( ptr->right, arr, i ); 10} 11 } Since InOrder2 is called a total of 2n + 1 times, it is ( n ) Getting Elements In Order (cont.)
170 Ensuring a Balanced BST Various schemes have been used to ensure a BST is balanced –red-black trees –AVL trees Insertion, searching, and deletion are all guaranteed to be O( lg n ), using the tightest big-oh notation
171 Ensuring a Balanced BST (cont.) The BST is faster, on average, than the red-black / AVL trees –BST’s are usually balanced –more work is required to ensure balancing However, the red-black / AVL trees give consistent performance –faster when BST’s are unbalanced –important in time-critical applications
172 Comparison of BST to Doubly-Linked List The hash table implementation of a doubly-linked list can be an alternative –under uniform hashing, a search can be done in ( 1 ) time –an insertion is done in ( 1 ) time (inserting at the head of the list) –under uniform hashing, a deletion is done in ( 1 ) time –what about providing elements in order?
173 Comparison of BST to Doubly-Linked List (cont.) A linked list can be sorted without affecting the time complexity of the sorting algorithm (as in the previous chapter) It generally takes ( n lg n ) time to sort a linked list It is possible to sort in ( n ) time (under favorable conditions) Once sorted, the elements can be provided in order
174 Since BST’s are usually balanced, it generally takes about ( n lg n ) time to insert n elements into a BST It takes ( n ) time to insert n elements at the head of a doubly-linked list The one to select, BST or doubly-linked list, depends on the situation one is working with (as usual) Comparison of BST to Doubly-Linked List (cont.)
175 Graphs Graphs are a very general data structure A graph consists of a set of nodes called vertices Any vertex can point to any number of other vertices It is possible that no vertex in the graph points to any other vertex Each vertex may point to every other vertex, even if there are thousands of vertices
176 A Directed Graph (Digraph) A D B C E F Each pointer is called an edge
177 An Undirected Graph A D B C E F Each edge points in both directions – ex: A points to D and D points to A
178 Another Digraph A D B C E F Nodes A and F point to each other – it is considered improper to draw a single undirected edge between them
179 Weighted Graph Graphville Node Town Vertex City Pointerburgh Builder’s Paradise Binary Tree Valley
180 Graph Implementation A vertex A is said to be adjacent to a vertex B if there is an edge pointing from A to B Graphs can be implemented in a couple of popular ways: –adjacency matrix –adjacency list
181 Adjacency Matrix Nodes are given a key from 0 to n – 1 The adjacency matrix is a 2-dimensional array of bool type variables
182 F T T F F T F F T T T F F F F T F F F T F T T T F F F F F T Adjacency Matrix (cont.) T T F F F F
183 F T T F F T F F T T T F F F F T F F F T F T T T F F F F F T Adjacency Matrix (cont.) T T F F F F The row numbers give the vertex number of the vertex that an edge is pointing from
184 F T T F F T F F T T T F F F F T F F F T F T T T F F F F F T Adjacency Matrix (cont.) T T F F F F Example: Node 1 points to Node 2 (set to T)
185 F T T F F T F F T T T F F F F T F F F T F T T T F F F F F T Adjacency Matrix (cont.) T T F F F F Example: But Node 2 doesn’t point to Node 1 (set to F)
186 F T T F F T F F T T T F F F F T F F F T F T T T F F F F F T Adjacency Matrix (cont.) T T F F F F Note that we can construct a graph from the adjacency matrix – the vertices may not be drawn in the same locations as the original graph, but the connections will be the same
187 Adjacency List An adjacency list is an array of linked lists The vertices are numbered 0 through n – 1 Each index in the array corresponds to the number of a vertex The vertex (with an index number) is adjacent to every node in the linked list at that index
188 Adjacency List (cont.)
189 Adjacency List (cont.) Vertex 1 is adjacent to vertex 2, and vertex 1 is also adjacent to vertex 5
190 Adjacency List (cont.) Note: vertex 2 may not connect to vertex 5 in the graph: 1 2 5
191 Adjacency List (cont.) Vertex 3 has an empty linked list – it is not adjacent to any other vertices
192 Adjacency List for Weighted Graph Red font is for vertex numbers, black font is for weights
193 Vertex Info Ordinarily, the vertex info is not stored in a linked list node of an adjacency list The vertex info size may be huge, and many duplicates of it would be stored throughout the adjacency list, wasting space Instead, a separate array contains the vertex info objects; each index corresponds to a vertex number for easy look-up
194 Adjacency Matrix vs. Adjacency List The speed of the adjacency matrix or the adjacency list depends on the algorithm –some algorithms need to know if there is a direct connection between two specific vertices – the adjacency matrix would be faster –some algorithms are written to process the linked list of an adjacency list, node by node – the adjacency list would be faster
195 Adjacency Matrix vs. Adjacency List (cont.) When both seem equally fast for a certain algorithm, then we consider memory usage We will consider the space complexity of each implementation Space complexities are like time complexities, but they tell us the effects on memory space usage as the problem size is varied
196 An array of vertex info is used for each implementation, which is ( n ) In the adjacency matrix, each dimension is n, so the spatial complexity is ( n 2 ) In the adjacency list, there are n elements in the array, giving a spatial complexity of ( n ) for the array Adjacency Matrix vs. Adjacency List (cont.)
197 Note that each edge corresponds to a linked list node in the adjacency list There can be no more than n( n – 1 ) edges in a graph, so the spatial complexity for the linked list nodes is O( n 2 ) the total spatial complexity for the adjacency list is O( n 2 ) Adjacency Matrix vs. Adjacency List (cont.)
198 Both of the spatial complexities for the adjacency matrix and the adjacency list absorb the ( n ) spatial complexity used in the vertex info array The spatial complexity of the adjacency list really depends on whether the graph is sparse or dense Adjacency Matrix vs. Adjacency List (cont.)
199 A sparse graph does not have that many edges relative to the number of vertices – if the number of edges is less than or equal to n, then the spacial complexity of the adjacency list is ( n ) In a dense graph, there may be close to n 2 edges, and then the spatial complexity of the adjacency list is ( n 2 ), the same as that for the adjacency matrix Adjacency Matrix vs. Adjacency List (cont.)