Top-Down Splay Trees

Chapter 4
Trees
Binary Search Trees


Definition
o Key in the root of the left subtree is less than (or equal to) the root.
o Key in the root of the right subtree is greater than the root.
o Left and right subtrees are binary search trees.
Figure 1 is an example:
Figure 1 Binary Search Tree




Where would you add X, B, and E?
At seats, build tree from the nodes: B R L M T C A N P D
What about deletion?
o Since it is easier to delete from a leaf, we first replace the node with its inorder
successor, and then delete the node that contained the inorder successor.
The code for insertion is as follows. I like to create the new node outside the insert
routine.
Binary Search Tree Class
#include <iostream>
#include <iomanip>
#include <string>
using namespace std;
template <class Etype>
class TreeNode
{
public:
Etype element;
TreeNode<Etype> *left;
TreeNode <Etype> *right;
TreeNode( Etype e = 0, TreeNode <Etype> * l = NULL, TreeNode <Etype> *r = NULL ) :
element( e ), left( l ), right( r ) { }
Binary Search Trees
Page 1
};
template <class Etype>
class BinarySearchTree
{
protected:
TreeNode<Etype> *root;
void makeEmpty( TreeNode<Etype> * & t );
bool insert(Etype & x, TreeNode<Etype> * & t );
Etype* find( const Etype & x, TreeNode<Etype> * t );
void printTree( TreeNode<Etype> * t ,string indent) const;
public:
BinarySearchTree( ) : root( NULL ) { }
void makeEmpty( ) { makeEmpty( root ); }
void printTree(string msg) const
{ cout << msg << endl;
printTree( root,"");
}
virtual Etype* find( const Etype & x ) { return find( x, root ); }
virtual int insert(Etype & x ) { return insert( x, root ); }
};
template <class Etype>
void BinarySearchTree<Etype>::
printTree( TreeNode<Etype> *t,string indent ) const
{
if (t==NULL) return;
printTree(t->right, indent+" ");
cout << indent << t->element << endl;
printTree(t->left, indent+" ");
}
// Return true if insertion is successful
template <class Etype>
bool BinarySearchTree<Etype>::
insert(Etype & x, TreeNode<Etype> * & t )
{
if( t == NULL )
{
t = new TreeNode<Etype>( x );
if (t == NULL) return false;
return true;
}
if( x < t->element ) return insert( x, t->left );
if( x > t->element ) return insert( x, t->right );
cout << "Warning, duplicate ignored " << x << endl;
return false; // does not allow duplicate insertions
}
Binary Search Trees
Page 2
template <class Etype>
Etype * BinarySearchTree<Etype>::
find( const Etype & x, TreeNode<Etype> * t )
{
if( t == NULL ) return NULL;
if( x < t->element ) return find( x, t->left );
if( x > t->element ) return find( x, t->right );
return &(t->element);
}
template <class Etype>
void BinarySearchTree<Etype>::
makeEmpty( TreeNode<Etype> * & t )
{
if( t != NULL )
{
makeEmpty( t->left );
makeEmpty( t->right );
delete t;
t = NULL;
}
}

Operations:
o Search – (h) , where h is the height of the tree. In the best case (balanced), h is
log n . In the worst case, h is n (the number of nodes in the tree).
o Insert – (h) , where h is the height of the tree.
o Delete – (h) , where h is the height of the tree.
 What makes deletion difficult?
Other Balanced Trees





Binary search trees are a good technique for finding elements in a dictionary.
But it has a disadvantage:
o In the worst case, the operations are still (n) .
There are applications that are time critical, and this amount of time is not acceptable.
We will consider balanced trees (AVL and Splay trees) where the worst case operation is
(log n) .
Go over recursion handout showing trace of recursion.
AVL Trees




Balanced tree – the height of left subtree and height of right subtree differ by at most one.
AVL trees are height balanced trees.
The name comes from the people that came up with the idea – Adelson-Velskii and
Landis.
Definition:
Binary Search Trees
Page 3
o An empty binary tree is an AVL tree.
o If T is a nonempty binary tree with TL and TR as its left and right subtrees, then T
is an AVL tree iff
 TL and TR are AVL trees and
 hL  hR  1 where hL and hR are the heights of TL and TR, respectively.




For several examples determine if AVL or not.
How bad can a tree be and still be AVL?
An AVL search tree is a binary search tree that is also an AVL tree.
See Figure 2 for some possible examples. For each tree:
o Is it an AVL tree?
o Is it an AVL search tree?
Figure 2 Possible AVL Trees and AVL Search Trees



Properties of AVL trees:
o The height of an AVL tree with n elements/node is (log n) .
o For every value of n, n  0 , there exists an AVL tree.
o An n-element AVL search tree can be searched in (height )  (log n) time.
o A new element can be inserted into an n-element AVL search tree so that the
result is an n  1 element AVL tree and such an insertion can be done in (log n)
time.
o An element can be deleted from an n-element AVL search tree so that the result is
an n  1 element AVL tree and such a deletion can be done in (log n) time.
From here on out, when I refer to an AVL tree, I mean an AVL search tree.
The bound in the height of an AVL tree can be shown by the recursive routine shown
below:
o
o
o
worst_tree (h) = /* Worst tree of height h */
root left = worst_tree(h-1)
root right = worst_tree(h-2)
Searching

How is this done? (Same way as with the binary search tree.)
Insertion

Insertions – just like BST, find the leaf where the node needs to go. After that node is
placed in the AVL tree, the tree is rebalanced if necessary.
Binary Search Trees
Page 4


We need to have a balance factor associated with each node x in the AVL tree.
height of left subtree of x – height of right subtree of x
See Figure 3 for an example of inserting 32 into an AVL tree and the subsequent
rebalancing. Notice that the balance factor is contained in each of the nodes shown in the
tree. Node 40 is too heavy to the left and its left child is heavy to the left. Thus, we need
a right rotation.
Figure 3 Example of Inserting into an AVL-Tree




Observations about the unbalanced tree that results from an insertion.
o In the unbalanced tree, the balance factors are limited to –2, –1, 0, 1, and 2.
o A node with balance factor 2 has a balance factor of 1 before the insertion.
Similarly, a node with balance factor –2, has a balance factor of –1 before the
insertion.
o The balance factors of only those nodes on the path from the root to the newly
inserted node can change as a result of the insertion.
o Let A denote the nearest ancestor of the newly inserted node whose balance factor
is either –2 or 2. The balance factor of all nodes on the path from A to the newly
inserted node was 0 prior to the insertion.
In Figure 3(b), what is A? (Node 40.)
There are four cases to consider when inserting into an AVL tree. We categorize these
starting from A [where A is the unhappy node].
 A is too heavy to the left, and its left subtree is heavy to the left. (right
rotation)
 A is too heavy to the left, and its left subtree is heavy to the right. (double
right rotation)
 A is too heavy to the right, and its right subtree is heavy to the right. (left
rotation)
 A is too heavy to the right, and its l right subtree is heavy to the left.
(double left rotation)

A generic heavy to the left type imbalance appears in Figure 4.
Binary Search Trees
Page 5
Figure 4 Right Rotation

Another type imbalance appears in Figure 5.
Figure 5 Double Right Rotation




The balance factor b in the tree is defined below:
b  0  bf ( B)  bf ( A)  0 after rotation.
b  1  bf ( B)  0 and bf ( A)  1 after rotation.
b  1  bf ( B)  1 and bf ( A)  0 after rotation.
The complexity of insertion into an AVL tree is (height )  (log n) .
Notice that one rotation (either single or double) is sufficient to restore balance if the
insertion causes imbalance.
Consider the tree in Figure 6.
o Insert 70
o Insert 22
Binary Search Trees
Page 6
Figure 6 AVL Insertion
Deletion


Deletions: Replace with inorder successor, delete, and rebalance.
imbalance at A is rectified by performing the rotation shown in Figure 7.
Figure 7 Right rotation


Following a rotation, we must continue to examine nodes on the path to the root. Unlike
the case of an insertion, one rotation may not suffice to restore balance following a
deletion. The number of rotations needed is (log n) .
The transformation needed when the imbalance is “too heavy to the left, but the left child
is heavy to the right” appears in Figure 8.
Binary Search Trees
Page 7
Figure 8 Double Right Rotation

Again, rotations may possibly need to continue to the root.
What happens if AVL property is extended so left and right differ by 2 or 3 or K?
o Termed height balanced tree, HB[K].
o As K increases, worst case height increases, but time to rebalance is less.
o There are symmetric rotations
Binary Search Trees
Page 8
Splay Trees



Splay trees support all the operations of binary trees.
But they do not guarantee (log N ) worst-case performance.
Instead, its bounds are amortized, meaning that although individual operations can be
expensive, any sequence of operations is guaranteed to behave as if each operation in the
sequence exhibited logarithmic behavior.
Self-adjusting and Amortized Analysis


Limitations of balanced search trees:
o Balanced search trees require storing an extra piece of information per node.
o They are complicated to implement, and as a result insertions and deletions are
expensive and potentially error-prone.
o We do not win when easy inputs occur.
Balanced search trees have a second feature that seems improvable:
o Their worst-case, average-case, and best-case performance are essentially
identical.
o It would be nice if the second access to the same piece of data was cheaper than
the first.
o The 90-10 rule – empirical studies suggest that in practice 90% of the accesses are
to 10% of the data items.
o It would be nice to get easy wins for the 90% case.
Amortized Time Bounds






Given the above information, what can we do?
Since the time to access in a binary tree is proportional to the depth of the accessed node,
we can attempt to restructure the tree by moving most recently accessed items toward the
root.
Even if there are intervening operations, we would expect the node to remain close to the
root and thus be quickly found.
We could call this strategy the rotate-to-root strategy.
Ends up having a O(log n) time bound for non-uniform access. For uniform access is
actually worse than balanced trees.
An application of the rotate-to-root strategy to node 3 is shown in Figure 9.
Binary Search Trees
Page 9
Figure 9 Rotate-to-Root


When nodes are rotated to the root, other nodes go to lower levels in the tree.
If we do not have the 90-10 rule, it is possible for a long sequence of bad accesses to
occur.
The Basic Bottom-Up Splay Tree



A technique called splaying can be used so a logarithmic amortized bound can be
achieved.
We use rotations such as we’ve seen before. When possible, we want to move the node
up two levels.
The zig case.
o Let X be a non-root node on the access path on which we are rotating.
o If the parent of X is the root of the tree, we merely rotate X and the root as shown
in Figure 10.
Figure 10 Zig Case

o This is the same as a normal single rotation.
The zig-zag case.
o In this case, X and both a parent P and a grandparent G. X is a right child and P is
a left child (or vice versa).
o This is the same as a double rotation.
o This is shown in Figure 11.
Binary Search Trees
Page 10
Figure 11 Zig-zag Case

The zig-zig case.
o This is a different rotation from those we have previously seen.
o Here X and P are either both left children or both right children.
o The transformation is shown in Figure 12.
o This is different from the rotate-to-root. Rotate-to-root rotates between X and P
and then between X and G. The zig-zig splay rotates between P and G and X and
P.
Figure 12 Zig-zig Case

Given the rotations, consider the example in Figure 13, where we are splaying c.
Binary Search Trees
Page 11
Figure 13 Example of Splay Tree
Basic Splay Tree Operations




Insertion – when an item is inserted, a splay is performed.
o As a result, the newly inserted item becomes the root of the tree.
Find – the last node accessed during the search is splayed
o If the search is successful, then the node that is found is splayed and becomes the
new root.
o If the search is unsuccessful, the last node accessed prior to reaching the NULL
pointer is splayed and becomes the new root.
FindMin and FindMax – perform a splay after the access.
DeleteMin and DeleteMax – these are important priority queue operations.
o
DeleteMin



o
Perform a FindMin.
This brings the minimum to the root, and by the binary search tree
property, there is no left child.
Use the right child as the new root and delete the node containing the
minimum.
DeleteMax
Binary Search Trees
Page 12



Peform a FindMax.
Set the root to the post-splay root’s left child and delete the post-splay
root.
Remove
o Access the node to be deleted bringing it to the root.
o Delete the root leaving two subtrees L (left) and R (right).
o Find the largest element in L using a FindMax operation, thus the root of L will
have no right child. (This is similar to replacing root with inorder predecessor)
o Make R the right child of L’s root.
o As an example, see Figure 14 that deletes 6.
Figure 14 Remove from Splay Tree
One bad operation is to find all the nodes in order.
Top-Down Splay Trees





Bottom-up splay trees are hard to implement.
We look at top-down splay trees that maintain the logarithmic amortized bound.
o This is the method recommended by the inventors of splay trees.
Basic idea – as we descend the tree in our search for some node X, we must take the
nodes that are on the access path, and move them and their subtrees out of the way. We
must also perform some tree rotations to guarantee the amortized time bound.
At any point in the middle of a splay, we have:
o The current node X that is the root of its subtree.
o Tree L that stores nodes less than X.
o Tree R that stores nodes larger than X.
Initially, X is the root of T, and L and R are empty.
Binary Search Trees
Page 13



As we descend the tree two levels at a time, we encounter a pair of nodes.
o Depending on whether these nodes are smaller than X or larger than X, they are
placed in L or R along with subtrees that are not on the access path to X.
When we finally reach X, we can then attach L and R to the bottom of the middle tree,
and as a result X will have been moved to the root.
We now just have to show how the nodes are placed in the different tree. This is shown
below:
o Zig rotation – Figure 15
Figure 15 Zig
o Zig-Zig – Figure 16
Figure 16 Zig-Zig
o Zig-Zag – Figure 17
Binary Search Trees
Page 14
Figure 17 Zig-Zag


Notice that the zig-zag is simplified.
o Making Y the root simplifies the coding because the action for the zig-zag
becomes identical to the zig case and would be advantageous, as testing for a host
of cases is time-consuming.
o The disadvantage is that a descent of only one level results in more iterations in
the splaying procedure.
The only thing we have to do now is reassemble the splay tree. This is shown in Figure
18
Figure 18 Reassembling

See Figure 19 for an example of these rotations when accessing 18.
Binary Search Trees
Page 15
Figure 19 Example 1 of Splaying

Another example when finding c is shown in Figure 20.
Binary Search Trees
Page 16
Figure 20 Example 2 of Splaying
4.7 B-Trees
4.8 Sets and Maps
Binary Search Trees
Page 17