160 likes | 263 Views
Data Structures CSCI 132, Spring 2014 Lecture 37 Binary Search Trees II. Definition of a Binary Search Tree. A binary search tree is either empty, or every node has a key for which the following are true: 1) The key of the root node is greater than any key in the left subtree.
E N D
Data StructuresCSCI 132, Spring 2014Lecture 37Binary Search Trees II
Definition of a Binary Search Tree A binary search tree is either empty, or every node has a key for which the following are true: 1) The key of the root node is greater than any key in the left subtree. 2) The key of the root node is less than any key in the right subtree. 3) The left and right subtrees are themselves binary search trees.
Inserting into a Binary Search Tree • To insert an item into a binary search tree, we must make sure that the tree maintains the binary search tree property. • We determine where an item may be inserted in a subtree by comparison with the item at the root of the subtree • If the item to be inserted has a value less than the value at the subroot, it must be inserted in the left subtree. • If the item to be inserted has a value greater than the value of the subroot, it must be inserted into the right subtree.
Implementing insert( ) template <class Record> Error_code Search_tree<Record> :: insert(const Record &new_data) { return search_and_insert(root, new_data); } template <class Record> Error_code Search_tree<Record> :: search_and_insert( Binary_node<Record> * &sub_root, const Record &new_data) { if (sub_root == NULL) { sub_root = new Binary_node<Record>(new_data); return success; } else if (new_data < sub_root->data) return search_and_insert(sub_root->left, new_data); else if (new_data > sub_root->data) return search_and_insert(sub_root->right, new_data); else return duplicate_error; }
tree_sort( ) 1. Build a binary search tree by using n calls to insert( ). 2. Print items out in order using inorder traversal. How long does it take? It depends on the tree: Short sort time: Long sort time:
Tree_sort( ) vs. quick sort • In tree sort, the first item is inserted into the root of the tree. • All subsequent items partitioned to the left or right depending on their relation to first item. • This is analogous to quick sort, if the first item in the list is used as the pivot for partition. • In tree sort, the second item becomes the root of a subtree. • It becomes the pivot to partition all subsequent items in that subtree. • This is analogous to quick sort for partitioning of one of the sublists.
Running time of tree_sort All comparisons for tree sort are done during the insert() calls. The insert() function does the same number of comparisons as quick sort. Therefore, tree sort has the same running time as quick sort: Worst case: Number of comparisons = O(n2) Average case: Number of comparisons = O(n lg n) Approximately: 1.39 n lg(n) + O(n)
Advantage of tree sort • Tree sort does not require that all the items are present in the list at the start of sorting. • Items can be added gradually as they become available. • Tree sort works on a linked structure that allows easier insertions and deletions than a contiguous list. • Disadvantage: In the worst case, tree sort is slow.
Removing a node from a binary search tree--case 1 Case 1: The node is a leaf. Replace the pointer to the node with Null. Then delete the node. sub_root data left right
remove( ) case 2 Case 2: The node has 1 non-NULL subtree. Replace link from parent to node with a link from the parent to the non-null subtree. Case 2a: sub_root Case 2b: sub_root data data left right left right data data left right left right . . . . . .
remove( ) case 3 Case 3: The node has 2 non-NULL subtrees. Find the immediate predecessor of the node by moving 1 branch to the left and then as far right as possible. Replace the node with its immediate predecessor. sub_root data right left data data left right left right . . . . . .
Examples of removing a node 10 15 5 2 7 12 17 3 8 1 6 13 4 1. Remove node 5. What replaces it? 2. Remove node 15. What replaces it?
Implementation of remove( ) template <class Record> Error_code Search_tree<Record> :: remove_root( Binary_node<Record>* &sub_root){ if (sub_root == NULL) return not_present; // Remember node to delete at end. Binary_node<Record> *to_delete = sub_root; if (sub_root->right == NULL) //cases 1 and 2a sub_root = sub_root->left; else if (sub_root->left == NULL) //case 2b sub_root = sub_root->right;
remove( ) continued else { // Neither subtree is empty. Case 3 to_delete = sub_root->left; // Move left to find predecessor. Binary_node<Record> *parent = sub_root; // parent of to_delete while (to_delete->right != NULL) { // to_delete is not the predecessor. parent = to_delete; to_delete = to_delete->right; } sub_root->data = to_delete->data; // Move from to_delete to root. if (parent == sub_root) sub_root->left = to_delete->left; else parent->right = to_delete->left; } delete to_delete; // Remove to_delete from tree. return success; }
search_and_destroy( ) template <class Record> Error_code Search_tree<Record> :: remove(const Record &target) { return search_and_destroy(root, target); } template <class Record> Error_code Search_tree<Record> :: search_and_destroy( Binary_node<Record>* &sub_root, const Record &target) { // find the node to remove and remove it. // we will work this out in class. }
search_and_destroy( ) template <class Record> Error_code Search_tree<Record> :: remove(const Record &target) { return search_and_destroy(root, target); } template <class Record> Error_code Search_tree<Record> :: search_and_destroy( Binary_node<Record>* &sub_root, const Record &target) { if (sub_root == NULL || sub_root->data == target) return remove_root(sub_root); else if (target < sub_root->data) return search_and_destroy(sub_root->left, target); else return search_and_destroy(sub_root->right, target); }