C Data Structures Binary Trees 1 Binary Search
C++ Data Structures Binary Trees 1
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 2
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 3
Binary Search Tree Node template <class Data. Type> BSTNode { Data. Type info; BSTNode<Data. Type> *left; BSTNode<Data. Type> *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. 4
Inserting Nodes Into a BST root: NULL BST starts off empty Objects that need to be inserted (only key values are shown): 37, 2, 45, 48, 41, 29, 20, 30, 49, 7 5
Inserting Nodes Into a BST (cont. ) root 37 37, 2, 45, 48, 41, 29, 20, 30, 49, 7 6
Inserting Nodes Into a BST (cont. ) root 37 2, 45, 48, 41, 29, 20, 30, 49, 7 7
Inserting Nodes Into a BST (cont. ) root 37 2, 45, 48, 41, 29, 20, 30, 49, 7 8
Inserting Nodes Into a BST (cont. ) root 37 2 < 37, so insert 2 on the left side of 37 2, 45, 48, 41, 29, 20, 30, 49, 7 9
Inserting Nodes Into a BST (cont. ) root 37 2 2, 45, 48, 41, 29, 20, 30, 49, 7 10
Inserting Nodes Into a BST (cont. ) root 37 2 45, 48, 41, 29, 20, 30, 49, 7 11
Inserting Nodes Into a BST (cont. ) root 37 2 45, 48, 41, 29, 20, 30, 49, 7 12
Inserting Nodes Into a BST (cont. ) root 37 2 45 > 37, so insert it at the right of 37 45, 48, 41, 29, 20, 30, 49, 7 13
Inserting Nodes Into a BST (cont. ) root 37 2 45 45, 48, 41, 29, 20, 30, 49, 7 14
Inserting Nodes Into a BST (cont. ) root 2 37 45 48, 41, 29, 20, 30, 49, 7 15
Inserting Nodes Into a BST (cont. ) root 2 37 45 48, 41, 29, 20, 30, 49, 7 16
Inserting Nodes Into a BST (cont. ) root 37 45 2 When comparing, we always start at the root node 48, 41, 29, 20, 30, 49, 7 17
Inserting Nodes Into a BST (cont. ) root 37 45 2 48 > 37, so look to the right 48, 41, 29, 20, 30, 49, 7 18
Inserting Nodes Into a BST (cont. ) root 37 45 2 48 > 37, so look to the right 48, 41, 29, 20, 30, 49, 7 19
Inserting Nodes Into a BST (cont. ) root 37 45 2 This time, there is a node already to the right of the root node. We then compare 48 to this node 48, 41, 29, 20, 30, 49, 7 20
Inserting Nodes Into a BST (cont. ) root 37 45 2 48 > 45, and 45 has no right child, so we insert 48 on the right of 45 48, 41, 29, 20, 30, 49, 7 21
Inserting Nodes Into a BST (cont. ) root 2 37 45 48 48, 41, 29, 20, 30, 49, 7 22
Inserting Nodes Into a BST (cont. ) root 2 37 45 48 41, 29, 20, 30, 49, 7 23
Inserting Nodes Into a BST (cont. ) root 2 37 45 48 41, 29, 20, 30, 49, 7 24
Inserting Nodes Into a BST (cont. ) root 37 45 2 48 41 > 37, so look to the right 41, 29, 20, 30, 49, 7 25
Inserting Nodes Into a BST (cont. ) root 2 37 45 48 41, 29, 20, 30, 49, 7 26
Inserting Nodes Into a BST (cont. ) root 37 45 2 48 41 < 45, so look to the left – there is no left child, so insert 41, 29, 20, 30, 49, 7 27
Inserting Nodes Into a BST (cont. ) root 37 45 2 41 48 41, 29, 20, 30, 49, 7 28
Inserting Nodes Into a BST (cont. ) root 37 45 2 41 48 29, 20, 30, 49, 7 29
Inserting Nodes Into a BST (cont. ) root 37 45 2 41 48 29, 20, 30, 49, 7 30
Inserting Nodes Into a BST (cont. ) root 37 45 2 29 < 37 41 48 29, 20, 30, 49, 7 31
Inserting Nodes Into a BST (cont. ) root 37 45 2 41 48 29, 20, 30, 49, 7 32
Inserting Nodes Into a BST (cont. ) root 37 45 2 29 > 2 41 48 29, 20, 30, 49, 7 33
Inserting Nodes Into a BST (cont. ) root 37 45 2 29 41 48 29, 20, 30, 49, 7 34
Inserting Nodes Into a BST (cont. ) root 37 45 2 29 41 48 20, 30, 49, 7 35
Inserting Nodes Into a BST (cont. ) root 37 45 2 29 41 48 20, 30, 49, 7 36
Inserting Nodes Into a BST (cont. ) root 37 45 2 29 41 48 20 < 37 20, 30, 49, 7 37
Inserting Nodes Into a BST (cont. ) root 37 45 2 29 41 48 20, 30, 49, 7 38
Inserting Nodes Into a BST (cont. ) root 37 45 2 29 41 48 20 > 2 20, 30, 49, 7 39
Inserting Nodes Into a BST (cont. ) root 37 45 2 29 41 48 20, 30, 49, 7 40
Inserting Nodes Into a BST (cont. ) root 37 45 2 29 41 48 20 < 29 20, 30, 49, 7 41
Inserting Nodes Into a BST (cont. ) root 37 45 2 29 41 48 20 20, 30, 49, 7 42
Inserting Nodes Into a BST (cont. ) root 37 45 2 29 41 48 20 30, 49, 7 43
Inserting Nodes Into a BST (cont. ) root 37 45 2 29 41 48 20 30, 49, 7 44
Inserting Nodes Into a BST (cont. ) root 37 45 2 29 20 41 48 30 < 37 30, 49, 7 45
Inserting Nodes Into a BST (cont. ) root 37 45 2 29 41 48 20 30, 49, 7 46
Inserting Nodes Into a BST (cont. ) root 37 45 2 29 20 41 48 30 > 2 30, 49, 7 47
Inserting Nodes Into a BST (cont. ) root 37 45 2 29 41 48 20 30, 49, 7 48
Inserting Nodes Into a BST (cont. ) root 37 45 2 29 20 41 48 30 > 29 30, 49, 7 49
Inserting Nodes Into a BST (cont. ) root 37 45 2 29 20 41 48 30 30, 49, 7 50
Inserting Nodes Into a BST (cont. ) root 37 45 2 29 20 41 48 30 49, 7 51
Inserting Nodes Into a BST (cont. ) root 37 45 2 29 20 41 48 30 49, 7 52
Inserting Nodes Into a BST (cont. ) root 37 45 2 29 48 41 49 > 37 20 30 49, 7 53
Inserting Nodes Into a BST (cont. ) root 37 45 2 29 20 41 48 30 49, 7 54
Inserting Nodes Into a BST (cont. ) root 37 45 2 29 48 41 49 > 45 20 30 49, 7 55
Inserting Nodes Into a BST (cont. ) root 37 45 2 29 20 41 48 30 49, 7 56
Inserting Nodes Into a BST (cont. ) root 37 45 2 29 48 41 49 > 48 20 30 49, 7 57
Inserting Nodes Into a BST (cont. ) root 37 45 2 29 20 41 30 48 49 49, 7 58
Inserting Nodes Into a BST (cont. ) root 37 45 2 29 20 41 30 48 49 7 59
Inserting Nodes Into a BST (cont. ) root 37 45 2 29 20 41 30 48 49 7 60
Inserting Nodes Into a BST (cont. ) root 37 45 2 29 7 < 37 20 41 30 48 49 7 61
Inserting Nodes Into a BST (cont. ) root 37 45 2 29 20 41 30 48 49 7 62
Inserting Nodes Into a BST (cont. ) root 37 45 2 29 7>2 20 41 30 48 49 7 63
Inserting Nodes Into a BST (cont. ) root 37 45 2 29 20 41 30 48 49 7 64
Inserting Nodes Into a BST (cont. ) root 37 45 2 29 7 < 29 20 41 30 48 49 7 65
Inserting Nodes Into a BST (cont. ) root 37 45 2 29 20 41 30 48 49 7 66
Inserting Nodes Into a BST (cont. ) root 37 45 2 29 7 < 20 20 41 30 48 49 7 67
Inserting Nodes Into a BST (cont. ) root 37 45 2 29 20 7 41 30 48 49 7 68
Inserting Nodes Into a BST (cont. ) root 37 45 2 29 20 41 30 48 49 7 69
Inserting Nodes Into a BST (cont. ) root 37 45 2 29 20 7 48 41 30 All elements have been inserted 49 70
Searching for a Key in a BST root 37 45 2 29 20 7 41 30 Searching for a key in a BST uses the same logic 48 49 71
Searching for a Key in a BST (cont. ) root 37 45 2 29 20 7 41 48 30 49 Key to search for: 29 72
Searching for a Key in a BST (cont. ) root 37 45 2 29 20 7 41 48 30 49 Key to search for: 29 73
Searching for a Key in a BST (cont. ) root 37 45 2 29 < 37 29 20 7 41 48 30 49 Key to search for: 29 74
Searching for a Key in a BST (cont. ) root 37 45 2 29 20 7 41 48 30 49 Key to search for: 29 75
Searching for a Key in a BST (cont. ) root 37 45 2 29 > 2 29 20 7 41 48 30 49 Key to search for: 29 76
Searching for a Key in a BST (cont. ) root 37 45 2 29 20 7 41 48 30 49 Key to search for: 29 77
Searching for a Key in a BST (cont. ) root 37 45 2 29 == 29 29 41 48 FOUND IT! 20 7 30 49 Key to search for: 29 78
Searching for a Key in a BST (cont. ) root 37 45 2 29 20 41 30 48 49 7 79
Searching for a Key in a BST (cont. ) root 37 45 2 29 20 7 41 48 30 49 Key to search for: 3 80
Searching for a Key in a BST (cont. ) root 37 45 2 29 20 7 41 48 30 49 Key to search for: 3 81
Searching for a Key in a BST (cont. ) root 37 45 2 3 < 37 29 20 7 41 48 30 49 Key to search for: 3 82
Searching for a Key in a BST (cont. ) root 37 45 2 29 20 7 41 48 30 49 Key to search for: 3 83
Searching for a Key in a BST (cont. ) root 37 45 2 3>2 29 20 7 41 48 30 49 Key to search for: 3 84
Searching for a Key in a BST (cont. ) root 37 45 2 29 20 7 41 48 30 49 Key to search for: 3 85
Searching for a Key in a BST (cont. ) root 37 45 2 3 < 29 29 20 7 41 48 30 49 Key to search for: 3 86
Searching for a Key in a BST (cont. ) root 37 45 2 29 20 7 41 48 30 49 Key to search for: 3 87
Searching for a Key in a BST (cont. ) root 37 45 2 3 < 20 29 20 7 41 48 30 49 Key to search for: 3 88
Searching for a Key in a BST (cont. ) root 37 45 2 29 20 7 41 48 30 49 Key to search for: 3 89
Searching for a Key in a BST (cont. ) root 37 45 2 3<7 29 20 7 41 48 30 49 Key to search for: 3 90
Searching for a Key in a BST (cont. ) root 37 45 2 When the child pointer you want to follow is set to NULL, the key 29 you are looking for is not in the BST 20 7 41 48 30 49 Key to search for: 3 91
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… 92
Bad Luck root 2 7 20 29 Exactly the same keys were inserted into this BST – but they were inserted in a different order (the order shown below) 30 2, 7, 20, 29, 30, 37, 41, 45, 48, 49 37 41 45 48 49 93
Bad Luck (cont. ) root 2 7 20 29 This is some bad luck, but a BST can be formed this way 30 37 41 45 48 2, 7, 20, 29, 30, 37, 41, 45, 48, 49 49 94
Bad Luck (cont. ) root 2 7 20 29 Using the “tightest” possible big-oh notation, the insertion and search time is O( n) 30 37 41 45 48 2, 7, 20, 29, 30, 37, 41, 45, 48, 49 49 95
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 96
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 97
Deletion Case 1: No Children root 37 45 2 29 20 48 41 30 Node 49 has no children – to delete it, we just remove it 49 98
Deletion Case 1: No Children (cont. ) root 37 45 2 29 20 41 48 30 99
Deletion Case 2: One Child root 37 45 2 29 20 41 30 Node 48 has one child – to delete it, we just splice it out 48 49 100
Deletion Case 2: One Child (cont. ) root 37 45 2 29 20 41 30 Node 48 has one child – to delete it, we just splice it out 48 49 101
Deletion Case 2: One Child (cont. ) root 37 45 2 29 20 41 30 49 102
Deletion Case 2: One Child (cont. ) root 37 45 2 29 20 41 30 48 Another example: node 2 has one child – to delete it we also splice it out 49 103
Deletion Case 2: One Child (cont. ) root 37 45 2 29 20 41 30 48 Another example: node 2 has one child – to delete it we also splice it out 49 104
Deletion Case 2: One Child (cont. ) root 37 45 29 20 41 30 48 49 105
Deletion Case 3: Two Children root 37 45 2 29 20 41 30 Node 37 has two children… 48 49 106
Deletion Case 3: Two Children (cont. ) root 37 45 2 29 20 41 30 to delete it, first we find the greatest node in its left subtree 48 49 107
Deletion Case 3: Two Children (cont. ) root 37 45 2 First, we go to the left once, then follow the right pointers as far as we can 29 20 41 30 to delete it, first we find the greatest node in its left subtree 48 49 108
Deletion Case 3: Two Children (cont. ) root 37 45 2 First, we go to the left once, then follow the right pointers as far as we can 29 20 41 30 to delete it, first we find the greatest node in its left subtree 48 49 109
Deletion Case 3: Two Children (cont. ) root 37 45 2 First, we go to the left once, then follow the right pointers as far as we can 29 20 41 30 to delete it, first we find the greatest node in its left subtree 48 49 110
Deletion Case 3: Two Children (cont. ) root 37 45 2 First, we go to the left once, then follow the right pointers as far as we can 29 20 41 30 to delete it, first we find the greatest node in its left subtree 48 49 111
Deletion Case 3: Two Children (cont. ) root 37 45 2 First, we go to the left once, then follow the right pointers as far as we can 29 20 41 30 30 is the greatest node in the left subtree of node 37 48 49 112
Deletion Case 3: Two Children (cont. ) root 37 45 2 29 20 41 30 Next, we copy the object at node 30 into node 37 48 49 113
Deletion Case 3: Two Children (cont. ) root 37 45 2 29 20 41 30 Next, we copy the object at node 30 into node 37 48 49 114
Deletion Case 3: Two Children (cont. ) root 30 45 2 29 20 41 30 Next, we copy the object at node 30 into node 37 48 49 115
Deletion Case 3: Two Children (cont. ) root 30 45 2 29 20 41 30 Finally, we delete the lower red node using case 1 or case 2 deletion 48 49 116
Deletion Case 3: Two Children (cont. ) root 30 45 2 29 20 41 48 49 117
Deletion Case 3: Two Children (cont. ) root 30 45 2 29 20 41 Let’s delete node 30 now 48 49 118
Deletion Case 3: Two Children (cont. ) root 30 45 2 29 20 41 48 49 119
Deletion Case 3: Two Children (cont. ) root 30 45 2 29 20 41 48 49 120
Deletion Case 3: Two Children (cont. ) root 30 45 2 29 20 41 The greatest node in the left subtree of node 30 48 49 121
Deletion Case 3: Two Children (cont. ) root 30 45 2 29 20 41 The greatest node in the left subtree of node 30 48 49 122
Deletion Case 3: Two Children (cont. ) root 29 45 2 29 20 41 The greatest node in the left subtree of node 30 48 49 123
Deletion Case 3: Two Children (cont. ) root 29 45 2 29 20 41 This time, the lower red node has a child – to delete it we use case 2 deletion 48 49 124
Deletion Case 3: Two Children (cont. ) root 29 45 2 29 20 41 This time, the lower red node has a child – to delete it we use case 2 deletion 48 49 125
Deletion Case 3: Two Children (cont. ) root 29 45 2 41 20 48 49 126
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 127
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 In. Order 2, and a driver for the recursive function called In. Order… 128
Getting Elements In Order 1 template <class Data. Type> 2 bool Binary. Search. Tree: : In. Order( Array<Data. Type> & arr ) 3 { 4 if ( size == 0 ) The client uses the driver 5 return false; called In. Order, passing in 6 arr. change. Size( size ); an Array by reference, 7 int i = 0; which will contain the 8 In. Order 2( root, arr, i ); objects in order by key after 9 return true; returning from In. Order 10 } 129
Getting Elements In Order (cont. ) 1 template <class Data. Type> 2 bool Binary. Search. Tree: : In. Order( Array<Data. Type> & arr ) 3 { If the BST has no elements, 4 if ( size == 0 ) false is returned 5 return false; 6 arr. change. Size( size ); 7 int i = 0; 8 In. Order 2( root, arr, i ); 9 return true; 10 } 130
Getting Elements In Order (cont. ) 1 template <class Data. Type> 2 bool Binary. Search. Tree: : In. Order( Array<Data. Type> & arr ) 3 { The size of the Array is 4 if ( size == 0 ) changed, so that it contains 5 return false; enough elements for all the 6 arr. change. Size( size ); nodes in the BST – the client 7 int i = 0; may have set the size of the 8 In. Order 2( root, arr, i ); Array correctly, but we like to 9 return true; play it safe 10 } 131
Getting Elements In Order (cont. ) 1 template <class Data. Type> 2 bool Binary. Search. Tree: : In. Order( Array<Data. Type> & arr ) 3 { 4 if ( size == 0 ) 5 return false; 6 arr. change. Size( size ); 7 int i = 0; i is used to index the Array 8 In. Order 2( root, arr, i ); 9 return true; 10 } 132
Getting Elements In Order (cont. ) 1 template <class Data. Type> 2 bool Binary. Search. Tree: : In. Order( Array<Data. Type> & arr ) 3 { The recursive function 4 if ( size == 0 ) In. Order 2 is called, passing 5 return false; 6 arr. change. Size( size ); arr by reference – when the top-level In. Order 2 returns, 7 int i = 0; arr will contain the objects 8 In. Order 2( root, arr, i ); with keys in sorted order 9 return true; 10 } 133
Getting Elements In Order (cont. ) 1 template <class Data. Type> 2 bool Binary. Search. Tree: : In. Order( Array<Data. Type> & arr ) 3 { 4 if ( size == 0 ) 5 return false; 6 arr. change. Size( size ); 7 int i = 0; So let’s now focus on the 8 In. Order 2( root, arr, i ); In. Order 2 function… 9 return true; 10 } 134
Getting Elements In Order (cont. ) 1 template <class Data. Type> 2 void Binary. Search. Tree: : In. Order 2( 3 BSTNode<Data. Type> *ptr, Array<Data. Type> & arr, int & i ) 4 { 5 if ( ptr != NULL ) { 6 In. Order 2( ptr->left, arr, i ); 7 arr[ i ] = ptr->info; 8 i++; 9 In. Order 2( ptr->right, arr, i ); 10 } 11 } 135
Getting Elements In Order (cont. ) 1 template <class Data. Type> 2 void Binary. Search. Tree: : In. Order 2( 3 BSTNode<Data. Type> *ptr, Array<Data. Type> & arr, int & i ) 4 { 5 if ( ptr != NULL ) { 6 In. Order 2( ptr->left, arr, i ); This is the only 7 arr[ i ] = ptr->info; line which places 8 i++; info in the array… 9 In. Order 2( ptr->right, arr, i ); 10 } 11 } 136
Getting Elements In Order (cont. ) 1 template <class Data. Type> 2 void Binary. Search. Tree: : In. Order 2( 3 BSTNode<Data. Type> *ptr, Array<Data. Type> & arr, int & i ) 4 { 5 if ( ptr != NULL ) { 6 In. Order 2( ptr->left, arr, i ); 7 arr[ i ] = ptr->info; then i is 8 i++; incremented 9 In. Order 2( ptr->right, arr, i ); 10 } 11 } 137
Getting Elements In Order (cont. ) 1 template <class Data. Type> 2 void Binary. Search. Tree: : In. Order 2( 3 BSTNode<Data. Type> *ptr, Array<Data. Type> & arr, int & i ) 4 { 5 if ( ptr != NULL ) { i is passed by 6 In. Order 2( ptr->left, arr, i ); reference, so 7 arr[ i ] = ptr->info; every time it is 8 i++; 9 In. Order 2( ptr->right, arr, i ); incremented, the calling function 10 } knows about it 11 } 138
Getting Elements In Order (cont. ) 1 template <class Data. Type> 2 void Binary. Search. Tree: : In. Order 2( 3 BSTNode<Data. Type> *ptr, Array<Data. Type> & arr, int & i ) 4 { 5 if ( ptr != NULL ) { The base case 6 In. Order 2( ptr->left, arr, i ); occurs when ptr 7 arr[ i ] = ptr->info; is NULL – just 8 i++; 9 In. Order 2( ptr->right, arr, i ); returns 10 } 11 } 139
Getting Elements In Order (cont. ) 1 template <class Data. Type> 2 void Binary. Search. Tree: : In. Order 2( 3 BSTNode<Data. Type> *ptr, Array<Data. Type> & arr, int & i ) 4 { 5 if ( ptr != NULL ) { There are two 6 In. Order 2( ptr->left, arr, i ); recursive calls 7 arr[ i ] = ptr->info; under the if – the 8 i++; first advances 9 In. Order 2( ptr->right, arr, i ); the pointer to the 10 } left child… 11 } 140
Getting Elements In Order (cont. ) 1 template <class Data. Type> 2 void Binary. Search. Tree: : In. Order 2( 3 BSTNode<Data. Type> *ptr, Array<Data. Type> & arr, int & i ) 4 { 5 if ( ptr != NULL ) { 6 In. Order 2( ptr->left, arr, i ); and the second 7 arr[ i ] = ptr->info; advances the 8 i++; pointer to the 9 In. Order 2( ptr->right, arr, i ); right child 10 } 11 } 141
Getting Elements In Order (cont. ) 1 template <class Data. Type> 2 void Binary. Search. Tree: : In. Order 2( 3 BSTNode<Data. Type> *ptr, Array<Data. Type> & arr, int & i ) 4 { 5 if ( ptr != NULL ) { 6 In. Order 2( ptr->left, arr, i ); Notice that this 7 arr[ i ] = ptr->info; will be the same 8 i++; ptr in both cases 9 In. Order 2( ptr->right, arr, i ); 10 } 11 } 142
Getting Elements In Order (cont. ) 1 template <class Data. Type> 2 void Binary. Search. Tree: : In. Order 2( 3 BSTNode<Data. Type> *ptr, Array<Data. Type> & arr, int & i ) 4 { 5 if ( ptr != NULL ) { Notice that each 6 In. Order 2( ptr->left, arr, i ); recursive call 7 arr[ i ] = ptr->info; approaches the 8 i++; 9 In. Order 2( ptr->right, arr, i ); base case… 10 } 11 } 143
Getting Elements In Order (cont. ) 1 template <class Data. Type> 2 void Binary. Search. Tree: : In. Order 2( 3 BSTNode<Data. Type> *ptr, Array<Data. Type> & arr, int & i ) 4 { it goes down one 5 if ( ptr != NULL ) { level through the 6 In. Order 2( ptr->left, arr, i ); tree, and so it 7 arr[ i ] = ptr->info; must get closer to 8 i++; the case where 9 In. Order 2( ptr->right, arr, i ); ptr->left or ptr 10 } >right is NULL 11 } 144
Getting Elements In Order (cont. ) 1 template <class Data. Type> 2 void Binary. Search. Tree: : In. Order 2( 3 BSTNode<Data. Type> *ptr, Array<Data. Type> & arr, int & i ) 4 { 5 if ( ptr != NULL ) { If the BST 6 In. Order 2( ptr->left, arr, i ); contains only 7 arr[ i ] = ptr->info; one node, does 8 i++; In. Order 2 work? 9 In. Order 2( ptr->right, arr, i ); 10 } 11 } 145
Getting Elements In Order (cont. ) 1 template <class Data. Type> 2 void Binary. Search. Tree: : In. Order 2( 3 BSTNode<Data. Type> *ptr, Array<Data. Type> & arr, int & i ) 4 { In this case, the 5 if ( ptr != NULL ) { 6 In. Order 2( ptr->left, arr, i ); root node is the only node – the 7 arr[ i ] = ptr->info; left and right 8 i++; 9 In. Order 2( ptr->right, arr, i ); pointers of the root node will be 10 } set to NULL. 11 } 146
Getting Elements In Order (cont. ) 1 template <class Data. Type> 2 void Binary. Search. Tree: : In. Order 2( 3 BSTNode<Data. Type> *ptr, Array<Data. Type> & arr, int & i ) 4 { 5 if ( ptr != NULL ) { 6 In. Order 2( ptr->left, arr, i ); 7 arr[ i ] = ptr->info; 8 i++; 9 In. Order 2( ptr->right, arr, i ); 10 } 11 } 147
Getting Elements In Order (cont. ) 1 template <class Data. Type> 2 void Binary. Search. Tree: : In. Order 2( 3 BSTNode<Data. Type> *ptr, Array<Data. Type> & arr, int & i ) 4 { ptr->left is NULL, 5 if ( ptr != NULL ) { so NULL is 6 In. Order 2( ptr->left, arr, i ); passed into ptr of 7 arr[ i ] = ptr->info; the new In. Order 2 8 i++; 9 In. Order 2( ptr->right, arr, i ); function that is made 10 } 11 } 148
Getting Elements In Order (cont. ) 1 template <class Data. Type> 2 void Binary. Search. Tree: : In. Order 2( 3 BSTNode<Data. Type> *ptr, Array<Data. Type> & arr, int & i ) 4 { Since ptr is NULL 5 if ( ptr != NULL ) { in the new 6 In. Order 2( ptr->left, arr, i ); In. Order 2 7 arr[ i ] = ptr->info; function, it will 8 i++; 9 In. Order 2( ptr->right, arr, i ); return right away 10 } 11 } 149
Getting Elements In Order (cont. ) 1 template <class Data. Type> 2 void Binary. Search. Tree: : In. Order 2( 3 BSTNode<Data. Type> *ptr, Array<Data. Type> & arr, int & i ) 4 { 5 if ( ptr != NULL ) { 6 In. Order 2( ptr->left, arr, i ); 7 arr[ i ] = ptr->info; 8 i++; 9 In. Order 2( ptr->right, arr, i ); 10 } 11 } 150
Getting Elements In Order (cont. ) 1 template <class Data. Type> 2 void Binary. Search. Tree: : In. Order 2( 3 BSTNode<Data. Type> *ptr, Array<Data. Type> & arr, int & i ) 4 { i is still 0 from the 5 if ( ptr != NULL ) { 6 In. Order 2( ptr->left, arr, i ); driver – ptr is the root at this level – 7 arr[ i ] = ptr->info; so the info at the 8 i++; 9 In. Order 2( ptr->right, arr, i ); root is placed into arr[ 0 ] 10 } 11 } 151
Getting Elements In Order (cont. ) 1 template <class Data. Type> 2 void Binary. Search. Tree: : In. Order 2( 3 BSTNode<Data. Type> *ptr, Array<Data. Type> & arr, int & i ) 4 { 5 if ( ptr != NULL ) { 6 In. Order 2( ptr->left, arr, i ); 7 arr[ i ] = ptr->info; i becomes 1 8 i++; 9 In. Order 2( ptr->right, arr, i ); 10 } 11 } 152
Getting Elements In Order (cont. ) 1 template <class Data. Type> 2 void Binary. Search. Tree: : In. Order 2( 3 BSTNode<Data. Type> *ptr, Array<Data. Type> & arr, int & i ) 4 { ptr->right is NULL 5 if ( ptr != NULL ) { 6 In. Order 2( ptr->left, arr, i ); – thus, the recursive function 7 arr[ i ] = ptr->info; call immediately 8 i++; 9 In. Order 2( ptr->right, arr, i ); returns as before 10 } 11 } 153
Getting Elements In Order (cont. ) 1 template <class Data. Type> 2 void Binary. Search. Tree: : In. Order 2( 3 BSTNode<Data. Type> *ptr, Array<Data. Type> & arr, int & i ) 4 { 5 if ( ptr != NULL ) { In. Order 2 6 In. Order 2( ptr->left, arr, i ); returns back to 7 arr[ i ] = ptr->info; the driver with 8 i++; 9 In. Order 2( ptr->right, arr, i ); the correct array 10 } 11 } 154
Getting Elements In Order (cont. ) 1 template <class Data. Type> 2 void Binary. Search. Tree: : In. Order 2( 3 BSTNode<Data. Type> *ptr, Array<Data. Type> & arr, int & i ) 4 { 5 if ( ptr != NULL ) { In. Order 2 works 6 In. Order 2( ptr->left, arr, i ); correctly when 7 arr[ i ] = ptr->info; there is only 8 i++; one node 9 In. Order 2( ptr->right, arr, i ); 10 } 11 } 155
Getting Elements In Order (cont. ) 1 template <class Data. Type> 2 void Binary. Search. Tree: : In. Order 2( 3 BSTNode<Data. Type> *ptr, Array<Data. Type> & arr, int & i ) 4 { So In. Order 2 works 5 if ( ptr != NULL ) { 6 In. Order 2( ptr->left, arr, i ); correctly when there is only one 7 arr[ i ] = ptr->info; node in the BST or 8 i++; 9 In. Order 2( ptr->right, arr, i ); the BST is empty (root is NULL) 10 } 11 } 156
Getting Elements In Order (cont. ) 1 template <class Data. Type> 2 void Binary. Search. Tree: : In. Order 2( 3 BSTNode<Data. Type> *ptr, Array<Data. Type> & arr, int & i ) 4 { Thus, it will work 5 if ( ptr != NULL ) { for a larger tree 6 In. Order 2( ptr->left, arr, i ); with 0 or 1 nodes 7 arr[ i ] = ptr->info; in the left subtree, 8 i++; and 0 or 1 nodes 9 In. Order 2( ptr->right, arr, i ); in the right 10 } subtree, etc. 11 } 157
Getting Elements In Order (cont. ) 1 template <class Data. Type> 2 void Binary. Search. Tree: : In. Order 2( 3 BSTNode<Data. Type> *ptr, Array<Data. Type> & arr, int & i ) 4 { Consider the 5 if ( ptr != NULL ) { 6 In. Order 2( ptr->left, arr, i ); general case: p nodes in the left 7 arr[ i ] = ptr->info; subtree and r 8 i++; 9 In. Order 2( ptr->right, arr, i ); nodes in the right subtree 10 } 11 } 158
Getting Elements In Order (cont. ) 1 template <class Data. Type> 2 void Binary. Search. Tree: : In. Order 2( 3 BSTNode<Data. Type> *ptr, Array<Data. Type> & arr, int & i ) 4 { We assume that 5 if ( ptr != NULL ) { In. Order 2 does 6 In. Order 2( ptr->left, arr, i ); what it is 7 arr[ i ] = ptr->info; supposed to do 8 i++; 9 In. Order 2( ptr->right, arr, i ); 10 } 11 } 159
Getting Elements In Order (cont. ) 1 template <class Data. Type> 2 void Binary. Search. Tree: : In. Order 2( 3 BSTNode<Data. Type> *ptr, Array<Data. Type> & arr, int & i ) 4 { The p nodes in the 5 if ( ptr != NULL ) { left subtree are all 6 In. Order 2( ptr->left, arr, i ); less than the root 7 arr[ i ] = ptr->info; node, and will 8 i++; occupy the first p 9 In. Order 2( ptr->right, arr, i ); positions of the 10 } array. 11 } 160
Getting Elements In Order (cont. ) 1 template <class Data. Type> 2 void Binary. Search. Tree: : In. Order 2( 3 BSTNode<Data. Type> *ptr, Array<Data. Type> & arr, int & i ) 4 { 5 if ( ptr != NULL ) { 6 In. Order 2( ptr->left, arr, i ); i is always 7 arr[ i ] = ptr->info; incremented after 8 i++; adding an element, 9 In. Order 2( ptr->right, arr, i ); so… 10 } 11 } 161
Getting Elements In Order (cont. ) 1 template <class Data. Type> 2 void Binary. Search. Tree: : In. Order 2( 3 BSTNode<Data. Type> *ptr, Array<Data. Type> & arr, int & i ) 4 { 5 if ( ptr != NULL ) { 6 In. Order 2( ptr->left, arr, i ); The root writes its 7 arr[ i ] = ptr->info; info correctly in 8 i++; position p + 1 of 9 In. Order 2( ptr->right, arr, i ); the array 10 } 11 } 162
Getting Elements In Order (cont. ) 1 template <class Data. Type> 2 void Binary. Search. Tree: : In. Order 2( 3 BSTNode<Data. Type> *ptr, Array<Data. Type> & arr, int & i ) 4 { Then, the last r 5 if ( ptr != NULL ) { nodes of the right 6 In. Order 2( ptr->left, arr, i ); subtree (all greater 7 arr[ i ] = ptr->info; than the root) are 8 i++; written in the last r 9 In. Order 2( ptr->right, arr, i ); positions of the 10 } array 11 } 163
Getting Elements In Order (cont. ) 1 template <class Data. Type> 2 void Binary. Search. Tree: : In. Order 2( 3 BSTNode<Data. Type> *ptr, Array<Data. Type> & arr, int & i ) 4 { Again, we assume 5 if ( ptr != NULL ) { In. Order 2 does 6 In. Order 2( ptr->left, arr, i ); what it is 7 arr[ i ] = ptr->info; supposed to do 8 i++; 9 In. Order 2( ptr->right, arr, i ); 10 } 11 } 164
Getting Elements In Order (cont. ) 1 template <class Data. Type> 2 void Binary. Search. Tree: : In. Order 2( 3 BSTNode<Data. Type> *ptr, Array<Data. Type> & arr, int & i ) 4 { Thus, if we assume 5 if ( ptr != NULL ) { the recursive call of 6 In. Order 2( ptr->left, arr, i ); In. Order 2 does what 7 arr[ i ] = ptr->info; it is supposed to 8 i++; do, we have 9 In. Order 2( ptr->right, arr, i ); established the 10 } correctness… 11 } 165
Getting Elements In Order (cont. ) 1 template <class Data. Type> 2 void Binary. Search. Tree: : In. Order 2( 3 BSTNode<Data. Type> *ptr, Array<Data. Type> & arr, int & i ) 4 { since In. Order 2 5 if ( ptr != NULL ) { works with smaller 6 In. Order 2( ptr->left, arr, i ); cases, we build 7 arr[ i ] = ptr->info; larger cases from 8 i++; the smaller cases, 9 In. Order 2( ptr->right, arr, i ); using the smaller 10 } cases as left and 11 } right subtrees 166
Getting Elements In Order (cont. ) 1 template <class Data. Type> 2 void Binary. Search. Tree: : In. Order 2( 3 BSTNode<Data. Type> *ptr, Array<Data. Type> & arr, int & i ) 4 { 5 if ( ptr != NULL ) { 6 In. Order 2( ptr->left, arr, i ); How do we know the time complexity 7 arr[ i ] = ptr->info; is ( n )? 8 i++; 9 In. Order 2( ptr->right, arr, i ); 10 } 11 } 167
Getting Elements In Order (cont. ) 1 template <class Data. Type> 2 void Binary. Search. Tree: : In. Order 2( 3 BSTNode<Data. Type> *ptr, Array<Data. Type> & arr, int & i ) 4 { In. Order 2 is called 5 if ( ptr != NULL ) { once from the driver. 6 In. Order 2( ptr->left, arr, i ); Then, for each node 7 arr[ i ] = ptr->info; in the tree, 2 calls to 8 i++; In. Order 2 are made, 9 In. Order 2( ptr->right, arr, i ); giving us a total of 10 } 2 n + 1 calls. 11 } 168
Getting Elements In Order (cont. ) 1 template <class Data. Type> 2 void Binary. Search. Tree: : In. Order 2( 3 BSTNode<Data. Type> *ptr, Array<Data. Type> & arr, int & i ) 4 { 5 if ( ptr != NULL ) { Since In. Order 2 is 6 In. Order 2( ptr->left, arr, i ); called a total of 7 arr[ i ] = ptr->info; 2 n + 1 times, it is 8 i++; ( n ) 9 In. Order 2( ptr->right, arr, i ); 10 } 11 } 169
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 170
Ensuring a Balanced BST (cont. ) • The BST is faster, on average, than the redblack / 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 171
Comparison of BST to Doubly-Linked List • The hash table implementation of a doublylinked 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? 172
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 173
Comparison of BST to Doubly-Linked List (cont. ) • 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) 174
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 175
A Directed Graph (Digraph) A C B F D Each pointer is called an edge E 176
An Undirected Graph A C B Each edge points in both directions – ex: A points to D and D points to A F D E 177
Another Digraph A F B Nodes A and F point to each other – it is considered improper to draw a single undirected edge between them D C E 178
Weighted Graphville 3 4 Node Town 8 Vertex City 4 Builder’s Paradise 10 4 Pointerburgh 2 Binary Tree Valley 179
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 180
Adjacency Matrix • Nodes are given a key from 0 to n – 1 • The adjacency matrix is a 2 -dimensional array of bool type variables 181
Adjacency Matrix (cont. ) 0 1 2 3 4 5 0 T T F F 1 F T T F F T 2 F F T T T F 3 F F F T F F 4 F T T T 5 F F F T 182
Adjacency Matrix (cont. ) 0 0 1 2 3 4 5 T T F F 1 F T T F F T 2 F F T T T F 3 F F F T F F 4 F T T T 5 F F F T The row numbers give the vertex number of the vertex that an edge is pointing from 183
Adjacency Matrix (cont. ) 0 1 2 3 4 5 0 T T F F 1 F T T F F T 2 F F T T T F 3 F F F T F F 4 F T T T 5 F F F T Example: Node 1 points to Node 2 (set to T) 184
Adjacency Matrix (cont. ) 0 1 2 3 4 5 0 T T F F 1 F T T F F T 2 F F T T T F 3 F F F T F F 4 F T T T 5 F F F T Example: But Node 2 doesn’t point to Node 1 (set to F) 185
Adjacency Matrix (cont. ) 0 1 2 3 4 5 0 T T F F 1 F T T F F T 2 F F T T T F 3 F F F T F F 4 F T T T 5 F F F T 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 186
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 187
Adjacency List (cont. ) 0 1 1 2 5 2 4 3 5 1 3 4 3 5 188
Adjacency List (cont. ) 0 1 1 2 2 5 4 3 5 1 3 4 Vertex 1 is adjacent to vertex 2, and vertex 1 is also adjacent to vertex 5 3 5 189
Adjacency List (cont. ) 0 1 1 2 5 2 4 3 Note: vertex 2 may not connect to vertex 5 in the graph: 2 1 3 4 5 1 3 5 5 190
Adjacency List (cont. ) 0 1 1 2 2 5 4 3 5 1 3 4 Vertex 3 has an empty linked list – it is not adjacent to any other vertices 3 5 191
Adjacency List for Weighted Graph 0 1 3 2 4 1 0 3 3 4 2 0 4 4 4 5 10 3 0 8 1 4 5 2 10 3 2 3 8 Red font is for vertex numbers, black font is for weights 192
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 193
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 194
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 195
Adjacency Matrix vs. Adjacency List (cont. ) • 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 196
Adjacency Matrix vs. Adjacency List (cont. ) • 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 ) 197
Adjacency Matrix vs. Adjacency List (cont. ) • 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 198
Adjacency Matrix vs. Adjacency List (cont. ) • 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 199
- Slides: 199