Week 8 Search Tree Algorithms
Week 8 Search Tree Algorithms
1/72
Tree Review
Binary search trees …
3/72
Balanced BSTs
Reminder …
4/72
Randomised BST Insertion
Effects of order of insertion on BST shape:
Tree ADT has no control over order that keys are supplied.
!"it cannot
insertRandom(tree,item)
| Input tree, item
| Output tree with item randomly inserted
|
| if tree is empty then
| return new node containing item
| end if
| // p/q chance of doing root insert
| if random number mod q < p then
| return insertAtRoot(tree,item)
| else
| return insertAtLeaf(tree,item)
| end if
Cost analysis:
!"similar to cost for inserting keys in random order: O(log2 n)
!"does not rely on keys being supplied in random order
9/72
Rebalancing Trees
An approach to balanced trees:
NewTreeInsert(tree,item):
| Input tree, item
| Output tree with item randomly inserted
|
| t=insertAtLeaf(tree,item)
| if #nodes(t) mod k = 0 then
| t=rebalance(t)
| end if
| return t
Note: To do this efficiently we would need to change tree data structure and basic operations:
Implementation of rebalance:
rebalance(t):
| Input tree t with n nodes
| Output t rebalanced
|
| if n≥3 then
| | t=partition(t, n/2 ) // put node with median key at root
| | left(t)=rebalance(left(t)) // then rebalance each subtree
| | right(t)=rebalance(right(t))
| end if
| return t
partition(tree,i):
| Input tree with n nodes, index i
| Output tree with item #i moved to the root
|
| m=#nodes(left(tree))
| if i < m then
| left(tree)=partition(left(tree),i)
| tree=rotateRight(tree)
| else if i > m then
| right(tree)=partition(right(tree),i-m-1)
| tree=rotateLeft(tree)
| end if
| return tree
1 is the root
Note: size(tree) = n, size(left(tree)) = m, size(right(tree)) = n-m-1 (why -1?)
Exercise #1: Partition 15/72
Does it solve the problem? … Not completely ⇒ Solution: real balanced trees (later)
Splay Trees
19/72
Splay Trees
A kind of "self-balancing" tree …
!"case 1: grandchild is left-child of left-child ⇒ double right rotation from top zig-zig
!"case 2: grandchild is right-child of left-child zig-zac
Rotate subtree, then rotate root
!"case 3: grandchild is left-child of right-child zig-zac
!"case 4: grandchild is right-child of right-child ⇒ double left rotation from top zig-zig
insertSplay(tree,item):
| Input tree, item
| Output tree with item splay-inserted
|
| if tree is empty then return new node containing item
| else if item=data(tree) then return tree
| else if item<data(tree) then
| | if left(tree) is empty then
| | left(tree)=new node containing item
| | else if item<data(left(tree)) then
| | // Case 1: left-child of left-child "zig-zig"
| | left(left(tree))=insertSplay(left(left(tree)),item)
| | tree=rotateRight(tree)
| | else if item>data(left(tree)) then
| | // Case 2: right-child of left-child "zig-zag"
| | right(left(tree))=insertSplay(right(left(tree)),item)
| | left(tree)=rotateLeft(left(tree))
| | end if
| | return rotateRight(tree)
| else // item>data(tree)
| | if right(tree) is empty then
| | right(tree)=new node containing item
| | else if item<data(right(tree)) then
| | // Case 3: left-child of right-child "zag-zig"
| | left(right(tree))=insertSplay(left(right(tree)),item)
| | right(tree)=rotateRight(right(tree))
| | else if item>data(right(tree)) then
| | // Case 4: right-child of right-child "zag-zag"
| | right(right(tree))=insertSplay(right(right(tree)),item)
| | tree=rotateLeft(tree)
| | end if
| | return rotateLeft(tree)
| end if
searchSplay(tree,item):
| Input tree, item
| Output address of item if found in tree
| NULL otherwise
|
| if tree=NULL then
| return NULL
| else
| | tree=splay(tree,item)
| | if data(tree)=item then
| | return tree
| | else
| | return NULL
| | end if
| end if
33/72
Better Balanced Binary Search Trees
So far, we have seen …
AVL Trees
35/72
AVL Trees
Invented by Georgy Adelson-Velsky and Evgenii Landis
Approach:
insertAVL(tree,item):
| Input tree, item
| Output tree with item AVL-inserted
|
| if tree is empty then
| return new node containing item
| else if item=data(tree) then
| return tree
| else
| | if item<data(tree) then
| | left(tree)=insertAVL(left(tree),item)
| | else if item>data(tree) then
| | right(tree)=insertAVL(right(tree),item)
| | end if
| | if height(left(tree))-height(right(tree)) > 1 then
| | if item>data(left(tree)) then
| | left(tree)=rotateLeft(left(tree)) Zig-Zac case, make it to Left - Left
| | end if
| | tree=rotateRight(tree)
| | else if height(right(tree))-height(left(tree)) > 1 then
| | if item<data(right(tree)) then
| | right(tree)=rotateRight(right(tree)) Zig-Zac case, make it to Right - Right
| | end if
| | tree=rotateLeft(tree)
| | end if
| | return tree
| end if
Note: Other tree implementations on this site differ slightly from ours
41/72
2-3-4 Trees
2-3-4 trees have three kinds of nodes
Search(tree,item):
| Input tree, item
| Output address of item if found in 2-3-4 tree
| NULL otherwise
|
| if tree is empty then
| return NULL
| else
| | i=0
| | while i<tree.order-1 and item>tree.data[i] do
| | i=i+1 // find relevant slot in data[]
| | end while
| | if item=tree.data[i] then // item found
| | return address of tree.data[i]
| | else // keep looking in relevant subtree
| | return Search(tree.child[i],item)
| | end if
| end if
46/72
Insertion into 2-3-4 Trees
Starting with the root node:
repeat
Insertion algorithm:
insert(tree,item):
| Input 2-3-4 tree, item
| Output tree with item inserted
|
| node=root(tree), parent=NULL
| repeat
| | if node.order=4 then
| | | promote = node.data[1] // middle value
| | | nodeL = new node containing node.data[0]
| | | nodeR = new node containing node.data[2]
| | | if parent=NULL then
| | | make new 2-node root with promote,nodeL,nodeR
| | | else
| | | insert promote,nodeL,nodeR into parent
| | | increment parent.order
| | | end if
| | | node=parent
| | end if
| | if node is a leaf then
| | insert item into node
| | increment node.order
| | else
| | | parent=node
| | | i=0
| | | while i<node.order-1 and item>node.data[i] do
| | | i=i+1 // find relevant child to insert item
| | | end while
| | | node=node.child[i]
| | end if
| until item inserted
Variation #1: why stop at 4? why not 2-3-4-5 trees? or M-way trees?
!"use standard BST nodes, augmented with one extra piece of data
!"implement similar strategy as 2-3-4 trees → red-black trees.
Red-Black Trees
55/72
Red-Black Trees
Red-black trees are a representation of 2-3-4 trees using BST nodes.
Link types:
Advantages:
56/72
Red-Black Trees
Definition of a red-black tree
!"a BST in which each node is marked red or black
!"no two red nodes appear consecutively on any path
!"a red node corresponds to a 2-3-4 sibling of its parent
!"a black node corresponds to a 2-3-4 child of its parent
#"if no parent (= root) → also black
!"all paths from root to leaf have same number of black nodes
RED = node is part of the same 2-3-4 node as its parent (sibling)
SearchRedBlack(tree,item):
| Input tree, item
| Output true if item found in red-black tree
| false otherwise
|
| if tree is empty then
| return false
| else if item<data(tree) then
| return SearchRedBlack(left(tree),item)
| else if item>data(tree) then
| return SearchRedBlack(right(tree),item)
| else // found
| return true
| end if
64/72
Red-Black Tree Insertion
Insertion is more complex than for standard BSTs
insertRB(tree,item,inRight):
| Input tree, item, inRight indicating direction of last branch
| Output tree with it inserted
|
| if tree is empty then
| return newNode(item)
| else if item=data(tree) then
| return tree
| end if
| if left(tree) and right(tree) both are RED then
| split 4-node in a red-black tree
| end if
| recursive insert a la BST, re-arrange links/colours after insert
| return modified tree
insertRedBlack(tree,item):
| Input red-black tree, item
| Output tree with item inserted
|
| tree=insertRB(tree,item,false)
| colour(tree)=BLACK
| return tree
Algorithm:
colour(left(currentTree))=BLACK
colour(right(currentTree))=BLACK
colour(currentTree)=RED
Algorithm:
if item<data(tree) then
left(tree)=insertRB(left(tree),item,false)
Insertion doesn't care it is Red-Black Tree
re-arrange links/colours after insert
Re-arrange: To balance back the tree
else // item larger than data in root
right(tree)=insertRB(right(tree),item,true)
re-arrange links/colours after insert
end if
Algorithm:
Symmetrically,
Symmetrically,
22, 12, 8, 15, 11, 19, 43, 44, 45, 42, 41, 40, 39
71/72
Red-black Tree Performance
Cost analysis for red-black trees:
72/72
Summary
!"Randomised at-leaf/at-root insertion
!"Tree operations
#"tree partition
#"joining trees
!"Self-adjusting trees
#"Splay trees
#"AVL trees
#"2-3-4 trees
#"Red-black trees
!"Suggested reading:
#"Sedgewick, Ch. 12.9
#"Sedgewick, Ch. 13.1-13.4