1
\
\
2
1
\
\
2
\
\
3
1
\
\
2
\
\
3
\
\
4
But if they arrive in non-random order, tree is likely to become
unbalanced:
Hence deep and inefficient to search.
Need to revise tree insertion mechanism to preserve reasonable balance. (Perfect balance is very expensive to maintain and actually impossible for the even number of nodes).
B
/ \
/ \
A D
/ \
/ \
C E
Bottom up insertion algorithms first insert the item, and then work back through the tree making changes which are necessary to keep the tree balanced. They are less efficient because they make two passes through the tree.
The difficult part is how to keep the rules satisfied while inserting a new
node. This is done top down, changing colours and moving (`rotating')
subtrees while searching for a place to insert the node.
The insertion algorithm for red-black trees is very complicated. Instead we will study in detail the algorithm for AVL trees (which is bottom up).
The difference is represented as a number equal to the depth of the
right subtree minus the depth of the left subtree.
In an AVL tree, only the following values are allowed:
A rotation is performed around some node X.
It can be
X A
/ / \
/ / \
A ==> B X
/ \ /
/ \ /
B C C
Right rotation around X:
X
/ \
/ \
A B
/ \
/ \
C D
\
\
E
Left rotation around X
gives:
B
/ \
/ \
X D
/ \ \
/ \ \
A C E
For example:
20
/ \
/ \
10 30
/
/
25
\
\
27
The tree with 30 as the top is unbalanced (balance factor -2)
but rotating it to the right gives another unbalanced tree:
20
/ \
/ \
10 25
\
\
30
/
/
27
20 20
/ \ / \
/ \ / \
10 30 10 30
/ /
/ ==> /
25 27
\ /
\ /
27 25
Then make right rotation around node 30:
20 20
/ \ / \
/ \ / \
10 30 10 27
/ / \
/ ==> / \
27 25 30
/
/
25
-2 -2
/ /
/ /
-1 0
/ / \
/ / \
2 2
\ \
\ \
1 0
\ / \
\ / \
Only give one of the cases (left or right), the other is symmetrical and replaced by ...
AVLTree avlInsert(ItemType item)
----------------------------
if(item <= value) // Insert into left subtree
if(left == null)
// No left daughter: add new subtree with balanceFactor = 0
left = InitializeAVL(item,0)
balanceFactor-- // Mark extra depth on left
else
oldbf = left.balanceFactor
left = left.avlInsert(item)
if ((left.balanceFactor != oldbf) and (left.balanceFactor != 0))
// Insertion changes balanceFactor on the left,
// and left is not perfectly balanced
balanceFactor--
else // Insert into right subtree
...
if ((balanceFactor < -1) or (balanceFactor > 1))
return this.rebalance()
else return this
AVLTree rebalance()
----------------
if (balanceFactor < -1)
// Left side heavy: rotate right
if(left.balanceFactor <= 0)
// Single rotation
return this.rotateRight()
else // Double rotation
left = left.rotateLeft()
return this.rotateRight()
if (balanceFactor > 1)
// Right side heavy: rotate left
...
AVLTree rotateLeft() ----------------- oldTop = this newTop = oldTop.right // Make rotation: oldTop.right = newTop.left newTop.left = oldTop // Update balance factors: otBF = oldTop.balanceFactor ntBF = newTop.balanceFactor newTop.balanceFactor = (ntBF - 1) - max(0, ntBF+1-otBF) oldTop.balanceFactor = (otBF - 1) - max(0, ntBF) return newTop
oldTop newTop
/| \ 1 1 / |\
/ | \ newTop oldTop / | \
/ | /|\ /|\ | \
/ | /|| \ / | \ | \
/ | / || \ / | |\ | d3 \
/ d1 | / || d3\ / | | \ |_____\
/______| / d2||____\ / d1 | |d2\
/____| /_____| |___\
The depth of the right subtree of oldTop is max(d2,d3) + 1.
AVL trees: height balanced binary search trees.
Leads to reasonable search complexity: O(log2N).
Efficient (but complicated) algorithms for maintaing height
balance under insertion and deletion.
Requires rotations to restore balance.