Lecture 13 notes

Download Report

Transcript Lecture 13 notes

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:
1 Each node may store two data items.
2 Each node may have three children.
3 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.
4 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.
5 All leaf nodes are on the same level.
Example 2-3 tree
400
300
200
330
370
450
420
Class Node23tree {
Node23tree firstChild;
Node23tree secondChild;
Node23tree thirdChild;
Node23tree parent;
int firstItem;
int secondItem;
.... class methods follow
}
500
600
470
700
530
570
650
750
800
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;
boolean found := 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
300
200
330
370
500
450
420
search for 250 stops here
600
470
700
530
570
650
750
800
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
750
300
200
330
370
450
420
The resulting tree:
600
470
700
530
570
650
750
800
500
400
200 250
850
300
450
330 370
420
700
600
470
800
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:
1 Adding an item in a leaf node with room to accommodate the new
item.
2 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.
3 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, branch is 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
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
where
*/
600
700
800
sibling
The splitNode method (contd.)
/* case 2: the splitting node is an internal node
middle := 700
where
sibling
600
800
splitting node
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
600
530
570
sibling
800
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
}