1 / 66

External Searching: B-Trees

External Searching: B-Trees. Dr. Jicheng Fu Department of Computer Science University of Central Oklahoma. Objectives (Section 11.3). The difference between internal and external searches Multiway Search Trees The definition of B-trees The search algorithm of B-trees

chava
Download Presentation

External Searching: B-Trees

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. External Searching: B-Trees Dr. Jicheng Fu Department of Computer Science University of Central Oklahoma

  2. Objectives (Section 11.3) • The difference between internal and external searches • Multiway Search Trees • The definition of B-trees • The search algorithm of B-trees • Insertion algorithms of B-trees • Deletion algorithms of B-trees

  3. Motivation • Internal search • The data structure is kept in RAM • E.g., binary search trees and AVL trees • External search • Locate and retrieve records stored in hard disks • B-trees are designed for external searches • B-trees stay in the hard disk

  4. Differences between RAM and hard disks • RAM • Access time: Microseconds • Access unit: Words (usually 4 bytes) • Hard disks • Access time: Milliseconds • Access units: Pages or blocks (usually 1 KB or more)

  5. Goal in external searching • Minimize the number of disk accesses • Disk access time is much longer than internal computation time • A disk block may have room for several records • A multiway decision can be used in a block • Multiway trees are more appropriate • Reduce the tree height, therefore reduce disk accesses

  6. Multiway Search Trees • An m-way search tree is a tree in which, for some integer m called the order of the tree, each node has at most m children • If k m is the number of children, then the node contains exactly k -1 keys, which partition all the keys into k subsets consisting of • all the keys less than the first key in the node • all the keys between a pair of keys in the node, and • all keys greater than the largest key in the node • A B-tree is an m-way tree

  7. A 5-way search tree

  8. More about m • m is decided by • Disk block size (s) • Record size (r) • Pointer size (p)

  9. Balanced Multiway Tree: The B-Tree • Motivation (minimizing disk accesses) • Reduce the height of the tree as much as possible • Solution • No empty subtrees appear above the leaves • The division of keys in subsets is efficient • All leaves stay on the same level • Guarantee the worst case performance • Every internal (non-leaf) node has at least some minimal number of children

  10. Definition • A B-tree of order m is an m-way search tree in which • All leaves are on the same level • All internal nodes except the root have at most m nonempty children, and at least m / 2 nonempty children (No overflow or underflow) • The number of keys in each internal node is one less than the number of its nonempty children, and these keys partition the keys in the children in the fashion of a search tree • The root has at most m children, but may have as few as two if it is not a leaf, or none if the tree consists of the root alone

  11. A B-tree of order 5

  12. C++ Implementation • For simplicity, the algorithms assume that the B-tree is constructed in RAM • Pointers are used to describe its structure • In real applications, • the addresses of disk blocks are used in place of the pointers, • taking a pointer reference becomes making a disk access, and • each tree node corresponds to a disk block • Auxiliary Disk I/O methods are needed

  13. Declaration of B_tree class • Users are allowed to specify: • the type of records to store in a B-tree • the order of a B-tree (m) template <class Record, int order> class B_tree { public: // Add public methods. private: // data members B_node<Record, order>* root; // Add private auxiliary functions here. };

  14. Declaration of B_node class template <class Record, int order> struct B_node { // data members: int count; Record data[order - 1]; B_node<Record, order>* branch[order]; // constructor: B_node( ); }; • Contiguous arrays are used to implement the entry (record) list and the children list (why?) • count gives the number of records in the B node • If count is nonzero then the node has count + 1 children

  15. About branch[] • branch[0] points to the subtree containing all records with keys less than that in data[0] • For 1  position  count - 1, branch[position] points to the subtree with keys strictly between data[position - 1] and data[position] • branch[count] points to the subtree with keys greater than that of data[count - 1]. • Constructor • Create an empty node (root) • Emptiness is implemented by setting count to 0 in the newly created node

  16. Searching Algorithm • The search process is very similar to that of the binary search tree • Public search_tree method template <class Record, int order> Error_code B_tree<Record, order> :: search_tree(Record &target) /* Post: If there is an entry in the B-tree whose key matches that in target, the parameter target is replaced by the corresponding Record from the B-tree and a code of success is returned. Otherwise a code of not_present is returned. Uses: recursive_search_tree */ { return recursive_search_tree(root, target); } • Auxiliary function recursive_search_tree

  17. template <class Record, int order> Error_code B_tree<Record, order> :: recursive_search_tree( B_node<Record, order>* current, Record &target) /* Pre: current is either NULL or points to a subtree of the B_tree. Post: If target key is not in the subtree, a code of not_present is returned. Otherwise, a code of success is returned and target is set to the corresponding Record of the subtree. Uses: recursive_search_tree recursively and search_node */ { Error_code result = not_present; int position; if (current != NULL) { result = search_node(current, target, position); if (result == not_present) result = recursive_search_tree(current->branch[position], target); else target = current->data[position]; } return result; }

  18. A search starts from the root and walks down through the tree • Similar to a search through a binary search tree • In a B-tree, each node is examined to find which branch to take next (search_node) • search_node seeks a target among the records stored in a current node • search_node uses an output parameter position • Output the index of the target if found within the current node, or • output the index of the branch to continue search

  19. Auxiliary function search_node template <class Record, int order> Error_code B_tree<Record, order> :: search_node( B_node<Record, order> *current, const Record &target, int &position) /* Pre: current points to a node of a B_tree. Post: If the Key of target is found in *current, then success is returned, the parameter position is set to the index of target, and the corresponding Record is copied to target. Otherwise, not_present is returned, and position is set to the branch index on which to continue the search. Uses: Methods of class Record. */ { position = 0; while (position < current->count && target > current->data[position]) position++; // Perform a sequential search through the keys. if (position < current->count && target == current->data[position]) return success; else return not_present; }

  20. Sequential search is used in search_node • If the record size is large in some applications, the order of the B-tree will be relatively small, and sequential search within the node is appropriate • For B-trees of large order, binary search may be used in search_node • Linked binary search trees may also be used instead of contiguous arrays • Red-black trees

  21. B-tree Insertion • B-trees grow in a bottom-up fashion • Due to the condition that all leaves be on the same level • Different from binary search trees • General idea • Search the tree for the new key • If the key is truly new, this search will terminate in failure at a leaf • Insert the new key into to the leaf node • If the node was not previously full, then the insertion is finished

  22. General idea (cont’d) • If the leaf node is full, the node splits into two nodes, side by side on the same level • When a node splits, insert the median key of the old leaf into its parent node • Repeat the splitting process in the parent node if necessary • When a key is added to a full root, then the root splits in two • The median key is sent upward becomes a new root • This is the only time when the B-tree grows in height

  23. Example: B-tree insertion (order: 5)

  24. Discussion • One splitting prepares the way for several simple insertions • When a node splits, it produces two half-full nodes • Later insertions are less likely to split nodes for a while • No matter in what order the keys arrive, the tree is always balanced

  25. Insertion Algorithms • Insertion into a B-tree can be naturally implemented as a recursive function • After insertion in a subtree, a (median) record may need to be reinserted higher in the tree • Recursion can keep track of the position within the tree and move back up the tree • An explicit auxiliary stack is not necessary

  26. The recursion function push_down needs three additional output parameters • current • The root of the current subtree under consideration. • If *current splits to accommodate new entry, push down returns overflow, and all 3 parameters are used: • The old node *current contains the left half of the entries • median gives the median record • right_branch points to a new node containing the right half of the former *current

  27. Public insertion method: insert • Needs only one parameter: new_entry • It calls the recursive method push_down • If the outermost call to function push_down returns overflow, then one record, median, remains to be reinserted into the B-tree • A new root must then be created to hold median • The height of the entire B-tree will increase • This is the only way that the B-tree grows in height

  28. template <class Record, int order> Error_code B_tree<Record, order> :: insert(const Record &new_entry) /* Post: If the Key of new_entry is already in the B-tree, a code of duplicate_error is returned. Otherwise, a code of success is returned and the Record new_entry is inserted into the B-tree in such a way that the properties of a B-tree are preserved Uses: Methods of struct B_node and the auxiliary function push_down. */ { Record median; B_node<Record, order> *right_branch, *new_root; Error_code result = push_down(root, new_entry, median, right_branch); (continued on next slide)

  29. if (result == overflow) { // The whole tree grows in height // Make a brand new root for the whole B-tree. new_root = new B_node<Record, order>; new_root->count = 1; new_root->data[0] = median; new_root->branch[0] = root; new_root->branch[1] = right_branch; root = new_root; result = success; } return result; }

  30. Auxiliary recursive function: push_down • Use a parameter current to point to the root of the subtree being searched • The condition current == NULL is used to terminate the recursion • Continue moving down the tree searching for new_entry until we hit an empty subtree • New_entry is not immediately inserted • overflow is returned and the new record is sent back up (called median) for later insertion • Because the B-tree does not grow by adding new leaves

  31. When a recursive call returns overflow, the record median needs to be inserted in the current node • If there is room, then insertion is finished • Otherwise, the node *current splits into *current and *right_branch and a new median is sent up the tree • The function uses three auxiliary functions • search_node (the same procedure used by searching) • push_in puts the median record into node *current provided that there is room • split splits a full node*current into two nodes that will be siblings on the same level in the B-tree

  32. template <class Record, int order> Error_code B_tree<Record, order> :: push_down( B_node<Record, order> *current, const Record &new_entry, Record &median, B_node<Record, order> * &right_branch) /* Pre: current is either NULL or points to a node of a B_tree. Post: If new_entry is found in the subtree to which current points, duplicate_error is returned. Otherwise, new_entry is inserted into the subtree: If this causes the height of the subtree to grow, overflow is returned, and the Record median is extracted to be reinserted higher in the B-tree, together with the subtree right_branch on its right. If the height does not grow, success is returned. Uses: push_down (recursive),search_node, split_node, and push_in*/ { Error_code result; int position; if (current == NULL) { // Since we cannot insert in an empty tree, the recursion terminates. median = new_entry; right_branch = NULL; result = overflow; } (continued on next slide)

  33. else { // Search the current node. if (search_node(current, new_entry, position) == success) result = duplicate_error; else { Record extra_entry; B_node<Record, order> *extra_branch; result = push_down(current->branch[position], new_entry, extra_entry, extra_branch); if (result == overflow) {// extra_entry must be added to current if (current->count < order - 1) { result = success; push_in(current, extra_entry, extra_branch, position); } else split_node( current, extra_entry, extra_branch, position, right_branch, median); // Record median and its right_branch will go up to a higher node } } } return result; }

  34. Auxiliary function: push_in

  35. template <class Record, int order> void B_tree<Record, order>::push_in(B_node<Record, order> *current, const Record &entry, B_node<Record, order> *right_branch, int position) /* Pre: current points to a node of a B_tree. The node *current is not full and entry belongs in *current at index position. Post: entry has been inserted along with its right-hand branch right_branch into *current at index position. */ { for (int i = current->count; i > position; i--) { // Shift all later data to the right. current->data[i] = current->data[i - 1]; current->branch[i +1] = current->branch[i]; } current->data[position] = entry; current->branch[position +1] = right_branch; current->count++; }

  36. Auxiliary function: split_node • Outline • Insert a record extra_entry with subtree pointer extra_branch into the full node *current • Split the right half off as a new node *right_half • Remove the median record and send it upward for reinsertion later • We cannot insert extra_entry directly into the full node • First determine whether extra_entry belongs in the left or right half and divide the node accordingly • Insert extra_entry into the appropriate half • Divide the node so that median is the largest entry in the left half

  37. template <class Record, int order> void B_tree<Record, order> :: split_node( B_node<Record, order> *current, // node to be split const Record &extra_entry, // new entry to insert B_node<Record, order> *extra_branch, // subtree on right of extra_entry int position, // index in node where extra_entry goes B_node<Record, order> * &right_half, // new node for right half of entries Record &median) // median entry (in neither half) /* Pre: current points to a node of a B_tree. The node *current is full, but if there were room, the record extra_entry with its right-hand pointer extra_branch would belong in *current at position, 0 position < order. Post: The node *current with extra_entry and pointer extra_branch at position are divided into nodes *current and *right_half separated by a Record median. Uses: Methods of struct B_node, function push_in. */ (continued on next slide)

  38. { right_half = new B_node<Record, order>; int mid = order/2; // The entries from mid on will go to right_half if (position <= mid) { // First case: extra_entry belongs in left half for (int i = mid; i < order - 1; i++) { // Move entries to right_half right_half->data[i - mid] = current->data[i]; right_half->branch[i +1 - mid] = current->branch[i +1]; } current->count = mid; right_half->count = order - 1 - mid; push_in(current, extra_entry, extra_branch, position); } (continued on next slide)

  39. else { // Second case: extra_entry belongs in right half mid++; // Temporarily leave the median in left half. for (int i = mid; i < order - 1; i++) { // Move entries to right_half right_half->data[i - mid] = current->data[i]; right_half->branch[i +1 - mid] = current->branch[i +1]; } current->count = mid; right_half->count = order - 1 - mid; push_in(right_half, extra_entry, extra_branch, position - mid); } median = current->data[current->count - 1]; // Remove median from left half. right_half->branch[0] = current->branch[current->count]; current->count--; }

  40. Deletion from a B-Tree • General method • If the entry to be deleted is not in a leaf, then its immediate predecessor (or successor) is guaranteed to be in a leaf • The immediate predecessor (or successor) is promoted into the position of the deleted entry, and the entry is deleted from the leaf • If the leaf contains more than the minimum number of entries, then one of them can be deleted with no further action

  41. If the leaf contains just the minimum number of records (underflow), then we first look at the two sibling leaves (or, in the case of a node on the outside, one leaf) that are immediately adjacent to each other and are children of the same node • If one of these has more than the minimum number of entries, then one of them can be moved into the parent node, and the entry from the parent moved into the leaf where the deletion is occurring

  42. If the adjacent leaf has only the minimum number of entries, then the two leaves and the median entry from the parent can all be combined as one new leaf, which will contain no more than the maximum number of entries allowed • If this step leaves the parent node with too few entries, then the process propagates upward. In the extreme case, the last entry is removed from the root, and then the height of the tree decreases

  43. Deletion Algorithms • Recursion is employed in the implementation of the deletion algorithm • If underflow occurs in a node, • we do not pull an entry down from a parent node during an inner recursive call, and • the recursive function is allowed to return even though there are too few entries in the node. • The outer call will then detect this underflow and move entries as required • When the last entry is removed from the root, then the empty node is deleted and the height of the B-tree shrinks

More Related