170 likes | 657 Views
Balanced search trees: 2-3 trees. 2-3 trees allow us to process ordered lists in more efficient way than binary trees with an ordering property. Recall that the worst case search efficiency in the later is O(N). All balanced trees guaranty at least O(logN) efficiency for
E N D
Balanced search trees: 2-3 trees. 2-3 trees allow us to process ordered lists in more efficient way than binary trees with an ordering property. Recall that the worst case search efficiency in the later is O(N). All balanced trees guaranty at least O(logN) efficiency for the search operation. Definition: A 2-3 tree is a general tree which satisfies the following properties: • Each node may store two data items. • Each node may have three children. • The second data item in any node may be empty, in which case sentinel value emptyFlag is stored there (assume emptyFlag := 0). If it is not empty, the first data item precedes the second one according to the specified ordering relationship. • For each node, data in the first child precedes the first data item in the node; data in the second child follows the first data item, but precedes the second; data in the third child follows the data in the second data item. • All leaf nodes are on the same level.
Example 2-3 tree 400 500 300 450 600 700 200 330 370 420 470 530 570 650 750 800 Class Node23tree { Node23tree firstChild; Node23tree secondChild; Node23tree thirdChild; Node23tree parent; int firstItem; int secondItem; .... class methods follow }
Search in 2-3 trees Algorithm search23tree (tree, target, precedes) Input: tree, a 2-3 tree; target, item sought; precedes, a comparator object Output: where, subtree containing the data; booleanfound := false if (tree != null) { boolean atLeastOneItem := (firstItem != emptyFlag) boolean twoItems := (secondItem != emptyFlag) if (atLeastOneItem) if (firstItem = target) { item := firstItem found := true } else if (twoItems) if (secondItem = target) { item := secondItem found := true }
Search in 2-3 trees (contd.) if (found || leafNode(tree) where := tree else if (atLeastOneItem) { if (precedes.lessThan(target, firstItem) search23tree (tree.firstChild, target, precedes) else if (! twoItems) search23tree (tree.secondChild, target, precedes) else if (precedes.lessThan(target, second Item) search23tree (tree.secondChild, target, precedes) else search23tree (tree.thirdChild, target, precedes) } else // the tree is emptry where := tree
Search in 2-3 trees (contd.) Efficiency of the search operation: Because leaf nodes are at the same level (by definition), we have a full tree. The maximal path length in a full tree is log n + 1. Therefore, the efficiency of the search operation in 2-3 tree is guaranteed to be logarithmic. In the best and average cases, it is better than logarithmic, because nodes contain more than one data item.
Insertion in 2-3 trees Insert 250 and 850 in the following tree: 400 500 300 450 600 700 200 330 370 420 470 530 570 650 750 800 search for 250 stops here search for 850 stops here To find where to insert, we must first search for 250. Search terminates in a node currently containing one item, 200. Therefore, we can insert 250 as a second item in that node. Search for 850 terminates in a node which already contains two items. We cannot create a new leaf node because this would violate the fullness of the tree. But we can split the node currently containing 750 and 800, into two “two” nodes (one containing 750, and the other containing 850). The middle value, 800, is recursively passed to the parent node, which may eventually have room to accommodate it.
Insertion example continued 700 600 800 800 400 500 750850 300 450 600 700 200 330 370 420 470 530 570 650 750 800 The resulting tree: 500 400 700 300 450 600 800 200 250 330 370 420 470 530 570 650 750 850
Insertion in 2-3 tree (contd.) In general, the following cases are possible when inserting an item in a 2-3 tree: • Adding an item in a leaf node with room to accommodate the new item. • Forcing a split with data being passed recursively up to their parent nodes until a node with one item is found to accommodate the passed data, or the root is split and the tree grows in height. • The first item is inserted in the empty tree. Before we develop the insertItem method, we must say how the split of a node is performed.
The splitNode method Algorithm splitNode(tree, where, data, branch, addBranch, precedes) Input: tree, a 2-3 tree; where, a subtree to be split; data, item forcing the split; branch, if addBranch is true, branchis a subtree to be added as a child to a tree node that is splitting; if addBranch is false, has no well defined value upon entry to the method; addBranch, a Boolean variable which is true if branch is added to a splitting node, and false if a leaf node is being split; precedes, a comparator object defining the ordering relationship. Output: tree, a 2-3 tree eventually with a new root; where, a subtree which now contains only one item and has a sibling to its right.
The splitNode method (contd.) /* case 1: splitting an external node / splitting the root node insert 700 parent, tree tree, where 600 800 where 600 800 */ if (where = tree) { Node23tree parent = new Node23tree() where.parent := parent parent.firstChild := where tree := parent } else parent := where.parent Node23tree sibling = new Node23tree () sibling.parent := parent parent.secondChild := sibling
The splitNode method (contd.) if (precedes.lessThan (data, where.firstItem)) { int middle := where.firstItem where.firstItem := data sibling.firstItem := where.secondItem where.secondItem := emptyFlag } else if (precedes.lessThan (data, where.secondItem)) { middle := data sibling.firstItem := where.secondItem where.secondItem := emptyFlag } else { sibling.firstItem := data middle := where.secondItem where.secondItem := emptyFlag } /* the result in our example parent 700 where 600 800 sibling */
The splitNode method (contd.) /* case 2: the splitting node is an internal node middle := 700 splitting node where sibling 600 800 branch 530 570 650 750 850 splitted node */ if (addBranch) { int key := branch.firstItem if (precedes.lessThan(key, where.firstChild.firstItem)) { sibling.secondChild := where.thirdChild sibling.firstChild := where.secondChild where.thirdChild := NULL where.secondChild := where.firstChild where.firstChild := branch sibling.secondChild.parent := sibling sibling.firstChild.parent := sibling where.firstChild.parent := where }
else if (precedes.lessThan(key, where.secondChild.firstItem)) { sibling.secondChild := where.thirdChild sibling.firstChild := where.secondChild where.thirdChild := NULL where.secondChild := branch sibling.secondChild.parent := sibling sibling.firstChild.parent := sibling where.secondChild.parent := where } else if (precedes.lessThan(key,where.thirdChild.firstItem)) { sibling.secondChild := where.thirdChild sibling.firstChild := branch where.thirdChild := NULL sibling.secondChild.parent := sibling sibling.firstChild.parent := sibling } else { sibling.secondChild := branch sibling.firstChild := where.thirdChild where.thirdChild := NULL sibling.secondChild.parent := sibling sibling.firstChild.parent := sibling }
/* the resulting tree parent 700 where sibling 600 800 530 570 650 750 850 Now 700 must be accomodated in the parent node; if not possible, the parent node must split further. */ if (parent.firstItem = emptyFlag) { parent.firstItem := middle parent.firstChild := where parent.secondChild := sibling } else if (parent.secondItem = emptyFlag) { if (precedes.lessThan(parent.firstItem, middle)) { parent.secondItem := middle parent.thirdChild := sibling } else {
parent.secondItem := parent.firstItem parent.firstItem := middle parent.thirdChild := parent.secondChild parent.secondChild := sibling } // end else else // split the parent node, which is an internal node splitNode(tree, parent, middle, sibling, true, precedes) // splitNode method end here
The insertItem method Algorithm insertItem(tree, newData, precedes) Input:tree, a 2-3 tree; newData, item to be inserted; precedes, a comparator object defining the ordering relationship. Output:tree, a 2-3 tree with newData inserted Node23tree leaf = search23tree (tree, newData, precedes) if (leaf.FirstItem = emptyFlag) //insertion in an empty tree tree.firstItem := newData else { //add newData in the leaf; if not possible, call splitNode to split the leaf boolean twoItems := (leaf.secondItem != emptyFlag) if (twoItems) splitNode(tree, leaf, newData, leaf, false, precedes) else if (precedes.lessThan(leaf.firstItem, newData) leaf.secondItem := newData else leaf.secondItem := leaf.firstItem leaf.firstItem := newData }