Search

Sponsored Links

Meta

Categories

Archives

Recent Posts

RSS Feeds

28
Dec

Balanced (AVL) Binary Search Trees

Related Blog Items

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

8 Comments

  • Anuj Verma  said:

    This is the best post I’d ever seen on balanced trees, keep the good work, thanks


  • mirchix  said:

    I’m becoming an admirer of this site :-)


  • Lucas  said:

    This is best work I’d ever seen on balanced trees, however the presentation lacks an order a bit


  • hello_programmer  said:

    probably not the best :-) but definitely a good one


  • SverreN  said:

    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);
    }


  • SverreN  said:

    I forgot to mention.

    Other than the suspected bug mentioned above your post (and code) is really good. Good work. :)


  • raj  said:

    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?


  • zul  said:

    I’m feel good after learning your source code than what I learned in the book. Good one.


Leave a comment

*
To prove you're a person (not a spam script), type the security word shown in the picture.
Anti-spam image