Balanced (AVL) Binary Search Trees
Related Blog Items
- Binary Search Trees
- Printing Binary Trees in Ascii
- Data structures lectures
- Binary decimal conversion....
- Binary to decimal
An AVL (adelson-velskii-landis) tree also known as balanced tree is a binary search tree with a balance condition.
The balance condition is that for every node in the tree, the height of left and right subtrees can differ by at most 1. The condition ensures that the depth of the tree is O(log N).
9 9
/ \ / \
/ \ / \
/ \ / \
5 15 5 15
/ \ / / \ / \
2 6 / 2 6 / \
13 13 20
/ /
/ /
12 12
fig1:BST but fig2:BST and is balanced(AVL) tree
not balanced tree
as the node 15 in fig1 is out of balance because height of left sub tree is 2 and height of right sub tree is 0.
You might be wondering how to compute height, here it is
height(Tree) = MAX(height(left-sub-tree), height(right-sub-tree)) + 1;
for example, height(9) = MAX(height(5), height(15)) + 1;
height(5) = MAX(height(2), height(6)) + 1 = 1;
height(15) = MAX(height(13), -1) + 1 = 2
so, height(5) = MAX(1, 2) + 1 = 3;
Maintaining height information of each node is necessary to check the balancing condition in AVL (balanced) tree.
So, here is the AVL (balanced) tree node representation
struct Tree { Tree * left, Tree * right; int element; int height; };
Here are the operations which are possible on Balanced (AVL) tree ADT,
1)Make Empty
2)Height
3)Find
4)FindMin
5)FindMax
6)Insert
7)Delete
All operations can be performed in O(log N) time, except possibly insertion. when we do an insertion, we need to update the balancing information for the nodes on the path back to root, but the reason that insertion is potentially difficult is that inserting a node could voilate the AVL (balanced) tree property. For example inserting 10 in fig2 would voilate the AVL tree property.
So, the inorder to maintain the balancing condition the tree has to be modified, this modification operation is called rotation.
Our algorithms used tail recursion, as the average depth of a binary search tree is O(log N),
the amount of stack space used is expected to be only O(log N).
1. Make Empty
This operation is for mainly intialization and can be used to destroy entire search tree also.
Tree *make_empty(Tree *t) { if (t != NULL) { make_empty(t->left); make_empty(t->right); free(t); t = NULL; } return NULL; }
2. Height
Finds height of the tree
int height(Tree *t) { if (t == NULL) { return -1; } else { return t->height; } }
3. Find
This operation returns a Tree node pointer in the search tree that has key X, or returns NULL if there is such node.
We make a recursive call on sub tree of the tree , either left or right, depending on the relationship of key X to the
element stored in the tree node
Tree *find(int elem, Tree *t) { if (t == NULL) { return NULL; } if (elem < t->element) { return find(elem, t->left); } else if (elem > t->element) { return find(elem, t->right); } else { return t; } }
4. FindMin
This operation returns the smallest element node in the tree, i.e. left most element of the tree
Tree *find_min(Tree *t) { if (t == NULL) { return NULL; } else if (t->left == NULL) { return t; } else { return find_min(t->left); } }
5. FindMax
This operation returns the biggest element node in the tree, i.e. right most element of the tree
Tree *find_max(Tree *t) { if (t == NULL) { return NULL; } else if (t->right == NULL) { return t; } else { return find_max(t->right); } }
6. Insert
This operation applies a Find operation on the tree, if its found, does nothing., otherwise,
insert the element at the last spot on the path traversed. After insertion, only nodes that are on the path from the insertion point to the root might have their balance altered because only those nodes have their subtrees altered. As we follow up the path and update the balancing information, we may find a node whose new balance voilates the AVL (balance) condition. We need to rebalance the tree to satisfy AVL property. A violation of the AVL property may occur in four cases: (N is a node to be rebalanced)
1) An insertion into the left subtree of the left child of the node N
2) An insertion into the right subtree of the left child of the node N
3) An insertion into the left subtree of the right child of the node N
4) An insertion into the right subtree of the right child of the node N
cases 1 & 4 can be fixed by single rotation
cases 2 & 3 can be fixed by double rotation
single and double rotation will be explained after explaing insert and delete operations.
//Insert i into the tree t, duplicate will be discarded //Return a pointer to the resulting tree. Tree * insert(int value, Tree * t) { Tree * new_node; if (t == NULL) { new_node = (Tree *) malloc (sizeof (Tree)); if (new_node == NULL) { return t; } new_node->element = value; new_node->height = 0; new_node->left = new_node->right = NULL; return new_node; } else if (value < t->element) { t->left = insert(value, t->left); if (height(t->left) - height(t->right) == 2) { if (value < t->left->element) { t = single_rotation_with_left(t); } else { t = double_rotation_with_left(t); } } } else if (value > t->element) { t->right = insert(value, t->right); if (height(t->right) - height(t->left) == 2) { if (value > t->right->element) { t = single_rotation_with_right(t); } else { t = double_rotation_with_right(t); } } } //else duplicate, ignore it t->height = MAX(height(t->left), height(t->right)) + 1; return t; }
7. Delete
This is the complicated operation among other tree operations. If the node to be deleted is a leaf, it can be deleted immediately. If the node has one child, the node can deleted after its parents adjusts a pointer to bypass the node. While deleting a node which has two children, the general strategy is to replace the data of this node with the smallest data of the right subtree and recursively delete that node.
After deletion, only nodes that are on the path from the deletion point to the root might have their balance altered because only those nodes have their subtrees altered. As we follow up the path and update the balancing information, we may find a node whose new balance voilates the AVL (balance) condition.
We need to rebalance the tree to satisfy AVL property using single and double rotation operations in similar way as we did for insertion operation.
Tree * delete(int value, Tree * t) { //Deletes node from the tree // Return a pointer to the resulting tree Tree * x; Tree *tmp_cell; if (t==NULL) return NULL; if (value < t->element) { t->left = delete(value, t->left); if (height(t->right) - height(t->left) >= 2) { if (t->left && (value < t->left->element)) { t = double_rotation_with_right(t); } else { t = single_rotation_with_right(t); } } } else if (value > t->element) { t->right = delete(value, t->right); if (height(t->left) - height(t->right) >= 2) { if (t->right && (value > t->right->element)) { t = double_rotation_with_left(t); } else { t = single_rotation_with_left(t); } } } else if (t->left && t->right) { tmp_cell = find_min(t->right); t->element = tmp_cell->element; t->right = delete(t->element, t->right); } else { tmp_cell = t; if (t->left == NULL) t = t->right; else if (t->right == NULL) t = t->left; free(tmp_cell); tmp_cell = NULL; } return t; }
Single Rotation
Single Rotation fixes voilations cause by the below cases (N is a node to be rebalanced)
1) An insertion into the left subtree of the left child of the node N
4) An insertion into the right subtree of the right child of the node N
Single Raotation does as below..
k2 k1
/ \ / \
k1 z --> x k2
/ \ / \
x y y z
examples for single rotation:
Before single rotating left at 20
20 / 15 / 5
After single rotating left
15 / \ 5 20
Here is the code for single rotation..
//Single rotation left // k2 k1 // / \ / \ // k1 z --> x k2 // / \ / \ // x y y z Tree *single_rotation_with_left(Tree *k2) { Tree *k1; k1 = k2->left; k2->left = k1->right; k1->right = k2; k2->height = MAX(height(k2->left), height(k2->right)) + 1; k1->height = MAX(height(k1->left), height(k2->right)) + 1; //return new root return k1; } //Single rotation right // k2 k1 // / \ / \ // x k1 --> k2 z // / \ / \ // y z x y Tree *single_rotation_with_right(Tree *k2) { Tree *k1; k1 = k2->right; k2->right = k1->left; k1->left = k2; k2->height = MAX(height(k2->left), height(k2->right)) + 1; k1->height = MAX(height(k1->right), height(k2->left)) + 1; //return new root return k1; }
Double Rotation
Single Rotation fixes voilations cause by the below cases (N is a node to be rebalanced)
2) An insertion into the right subtree of the left child of the node N
3) An insertion into the left subtree of the right child of the node N
Double Raotation does as below..
k3 k2
/ \ / \
k1 d --> k1 k3
/ \ / \ / \
a k2 a b c d
/ \
b c
examples for double rotation:
Before double rotating right at 10
10
/ \
5 15
/ \
12 16
\
13
After double rotating right
12
/ \
10 15
/ / \
5 13 16
Here is the code for single rotation..
//Double rotation left // k3 k2 // / \ / \ // k1 d --> k1 k3 // / \ / \ / \ // a k2 a b c d // / \ // b c Tree *double_rotation_with_left(Tree *k3) { //rotate between k1 & k2 k3->left = single_rotation_with_right(k3->left); //rotate between k3 & k2 return single_rotation_with_left(k3); } //Double rotation right // k3 k2 // / \ / \ // a k1 --> k3 k1 // / \ / \ / \ // k2 d a b c d // / \ // b c Tree *double_rotation_with_right(Tree *k3) { //rotate between k1 & k2 k3->left = single_rotation_with_left(k3->right); //rotate between k3 & k2 return single_rotation_with_right(k3); }
Here are the operations examples..
After inserting element 20..
20
After inserting element 5..
20 / 5
After inserting element 15..
15 / \ 5 20
After inserting elements 9, 13..
15 / \ 9 20 / \ 5 13
After inserting elements 2, 6, 12, 14, ..
9
/ \
/ \
/ \
5 15
/ \ / \
2 6 / \
13 20
/ \
/ \
12 14
After inserting elements 15..
9
/ \
/ \
/ \
5 15
/ \ / \
2 6 / \
13 20
/ \
/ \
12 14
After inserting elements 16..
9
/ \
/ \
/ \
5 15
/ \ / \
2 6 / \
/ \
13 20
/ \ /
/ \ 16
12 14
After inserting elements 17..
9
/ \
/ \
/ \
5 15
/ \ / \
2 6 / \
/ \
/ \
13 17
/ \ / \
/ \ / \
12 14 16 20
After inserting elements 18..
15
/ \
/ \
/ \
/ \
9 17
/ \ / \
/ \ / \
/ \ 16 20
5 13 /
/ \ / \ 18
2 6 / \
12 14
After inserting elements 19..
15
/ \
/ \
/ \
/ \
9 17
/ \ / \
/ \ / \
/ \ 16 19
5 13 / \
/ \ / \ / \
2 6 / \ 18 20
12 14
After deleting a node (14)
15
/ \
/ \
/ \
/ \
9 17
/ \ / \
/ \ / \
/ \ 16 19
5 13 / \
/ \ / / \
2 6 12 18 20
After deleting a node (13)
15
/ \
/ \
/ \
9 17
/ \ / \
5 12 / \
/ \ 16 19
2 6 / \
/ \
18 20
After deleting a node (5)
15
/ \
/ \
/ \
9 17
/ \ / \
6 12 / \
/ 16 19
2 / \
/ \
18 20
After deleting a node (10)
15
/ \
/ \
/ \
9 17
/ \ / \
6 12 / \
/ 16 19
2 / \
/ \
18 20
After deleting a node (14, 15)
16
/ \
/ \
/ \
9 19
/ \ / \
6 12 / \
/ 17 20
2 \
18
After deleting a node (16)
17
/ \
/ \
/ \
9 19
/ \ / \
6 12 / \
/ 18 20
2
After deleting a node (19)
17
/ \
/ \
/ \
9 20
/ \ /
6 12 18
/
2
After deleting a node (18, 20)
9
/ \
/ \
6 17
/ /
2 12
Download the AVL (Balanced) Tree Source Code [note: I’ve used some what old compiler(msvc6.0), if you are using a new compiler you may get some compilation errors, fix them yourself otherwise drop a message over here]
Popularity: 28%
You need to log on to convert this article into PDF
Related Blog Items - Binary Search Trees
- Printing Binary Trees in Ascii
- Data structures lectures
- Binary decimal conversion....
- Binary to decimal
Related Blog Items
- Binary Search Trees
- Printing Binary Trees in Ascii
- Data structures lectures
- Binary decimal conversion....
- Binary to decimal
This is the best post I’d ever seen on balanced trees, keep the good work, thanks
December 29th, 2007 at 12:26 pm
I’m becoming an admirer of this site :-)
January 1st, 2008 at 2:59 pm
This is best work I’d ever seen on balanced trees, however the presentation lacks an order a bit
January 10th, 2008 at 10:26 am
probably not the best :-) but definitely a good one
January 22nd, 2008 at 11:10 pm
Isn’t there a bug in your sources?
It seems to me that in your function double_rotation_with_right you should use:
k->right = single_rotation_with_left(k3->right);
and not:
k->left = single_rotation_with_left(k3->right);
You can easilly test this out using with this example insertion sequence:
5, 10, 8
Obviously this will require balancing. To me it seems like your code will mess up the binary tree, creating a circular reference when you balance after having inserted the 8.
Proposed correct code:
Tree *double_rotation_with_right(Tree *k3)
{
//rotate between k1 & k2
k3->right = single_rotation_with_left(k3->right);
//rotate between k3 & k2
return single_rotation_with_right(k3);
}
May 2nd, 2008 at 5:55 pm
I forgot to mention.
Other than the suspected bug mentioned above your post (and code) is really good. Good work. :)
May 2nd, 2008 at 5:58 pm
I fail to see this case clearly - i.e,
In the insert into tree snippet, will this condition be met - if (height(t->left) - height(t->right) == 2)?
All I see is this
new_node->height = 0; but no updates to the height for each node are done, so how will this work. Am I missing something here?
May 14th, 2008 at 8:24 am
I’m feel good after learning your source code than what I learned in the book. Good one.
June 4th, 2008 at 2:50 pm