next up previous
Next: About this document Up: My Home Page

AVL Trees
Lecture 24

Steven S. Skiena

AVL Trees

An AVL tree is a binary search tree in which the heights of the left and right subtrees of the root differ by at most 1, and the left and right subtrees are again AVL trees.

Therefore, we can label each node of an AVL tree with a balance factor as well as a key:

AVL trees are named after their inventors, the Russians G.M. Adel'son-Velshi, and E.M. Laudis in 1962.

These are the most unbalanced possible AVL trees with a skew always to the right.

By maintaining the balance of each node (i.e. the subtree below it) when we insert a new node, we can easily see whether or not to take action!

The balance is more useful than maintaining the height of each node because it is a relative, not absolute measure. Thus we can move subtrees around without affecting their balance, even if they end up at different heights.

How good are AVL trees?

To find out how bad they can be, we want to find what the minimum number of modes a tree of height h can have. If tex2html_wrap_inline175 is a minimum node AVL tree, its left and right subtrees must themselves be minimum node AVL trees of smaller size. Further, they should differ in height by 1 to take advantage of AVL freedom.

Counting the root node,

displaymath167

Such trees are called Fibonacci trees and tex2html_wrap_inline177 .

Thus the worse case AVL tree is almost as good as a random tree - on average it is very close to an optional tree.

Why are Fibonacci trees of logarithmic height?

Recall that the Fibonacci numbers are defined tex2html_wrap_inline179 , tex2html_wrap_inline181 , tex2html_wrap_inline183 .

displaymath168

Since we are adding the last two numbers together, we are more than doubling the next-to-last and somewhat less that doubling the last number.

In fact, tex2html_wrap_inline185 , so a tree with tex2html_wrap_inline187 nodes has height

displaymath169

AVL Trees Interface

 
INTERFACE AVLTree;          (*08.07.94. CW, LB*)
(* Balanced binary search tree, subtype of "BinaryTree.T" *)
 
  IMPORT BinaryTree;
  TYPE T <: BinaryTree.T;   (*T is a subtype of BinaryTree.T *)
 
END AVLTree.

AVL Trees Implementation

MODULE AVLTree EXPORTS AVLTree, AVLTreeRep;  (*08.07.94. CW*)
(* Implementation of the balanced binary search tree as subtype of
   "BinaryTree.T". The methods "insert" and "delete" are overwritten
   to keep the tree balanced when elements are inserted or
   deleted. The other methods are inhereted from the supertype.
*)

  IMPORT BinaryTree, BinTreeRep;

  REVEAL
    T = BinaryTree.T BRANDED OBJECT
        OVERRIDES
          delete:= Delete;
          insert:= Insert;
        END;

  PROCEDURE Insert(tree: T; e: REFANY) =

    PROCEDURE RR (VAR root: BinTreeRep.NodeT) = 
      (*simple rotation right*)
      VAR left:= root.left;
      BEGIN
        root.left:= left.right;
        left.right:= root;
        NARROW(root, NodeT).balance:= 0;
        root:= left;
      END RR;

    PROCEDURE RL (VAR root: BinTreeRep.NodeT) = 
      (*simple rotation left*)
      VAR right:= root.right;
      BEGIN
        root.right:= right.left;
        right.left:= root;
        NARROW(root, NodeT).balance:= 0;
        root:= right;
      END RL;

    PROCEDURE RrR (VAR root: BinTreeRep.NodeT) = 
      (*double rotation right*)
      VAR right:= root.left.right;
      BEGIN
        root.left.right:= right.left;
        right.left:= root.left;
        IF NARROW(right, NodeT).balance = -1
          THEN NARROW(root, NodeT).balance:= +1
          ELSE NARROW(root, NodeT).balance:= 0
        END;
        IF NARROW(right, NodeT).balance = +1
          THEN NARROW(root.left, NodeT).balance:= -1
          ELSE NARROW(root.left, NodeT).balance:= 0
        END;
        root.left:= right.right;
        right.right:= root;
        root:= right;
      END RrR;

    PROCEDURE RrL (VAR root: BinTreeRep.NodeT) = 
      (*double rotation left*)
      VAR left:= root.right.left;
      BEGIN
        root.right.left:= left.right;
        left.right:= root.right;
        IF NARROW(left, NodeT).balance = +1
          THEN NARROW(root, NodeT).balance:= -1
          ELSE NARROW(root, NodeT).balance:= 0
        END;
        IF NARROW(left, NodeT).balance = -1
          THEN NARROW(root.right, NodeT).balance:= +1
          ELSE NARROW(root.right, NodeT).balance:= 0
        END;
        root.right:= left.left;
        left.left:= root;
        root:= left;
      END RrL;

    PROCEDURE InsertBal(VAR root: BinTreeRep.NodeT; new: REFANY;
                        VAR bal: BOOLEAN) =
      BEGIN
        IF root = NIL
          THEN
            root:= NEW(NodeT, info:= new, balance:= 0);

          ELSIF tree.compare(new, root.info)<0 THEN
            InsertBal(root.left, new, bal);
            IF NOT bal THEN  (* bal stops recursion*)
              WITH done=NARROW(root, NodeT).balance DO
                CASE done OF
                |+1=> done:= 0; bal:= TRUE;   (*insertion ok*)
                | 0=> done:= -1;              (*still balanced, but continue*)
                |-1=>
                       IF NARROW(root.left, NodeT).balance = -1
                         THEN RR(root)
                         ELSE RrR(root)
                       END;
                       NARROW(root, NodeT).balance:= 0;
                       bal:= TRUE;            (*after rotation tree ok*)
                END; (*CASE*)
              END (*WITH*)
            END (*IF*)

          ELSE
            InsertBal(root.right, new, bal);
            IF NOT bal THEN    (* bal is set to stop the recurs. adjustm. of balance *)
              WITH done=NARROW(root, NodeT).balance DO
                CASE done OF
                |-1=> done:= 0; bal:= TRUE;   (*insertion ok *)
                | 0=> done:= +1;              (*still balanced, but continue*)
                |+1=>
                       IF NARROW(root.right, NodeT).balance = +1
                         THEN RL(root)
                         ELSE RrL(root)
                       END;
                       NARROW(root, NodeT).balance:= 0;
                       bal:= TRUE;           (*after rotation tree ok*)
                END; (*CASE*)
              END (*WITH*)
            END (*IF*)
        END;
      END InsertBal;

    VAR balanced:= FALSE;
    BEGIN (*Insert*)
      InsertBal(tree.root, e, balanced)
    END Insert;

  PROCEDURE Delete(tree: T; e: REFANY): REFANY =

    PROCEDURE RR (VAR root: BinTreeRep.NodeT;
                  VAR bal: BOOLEAN) = 
      (*simple rotation right*)
      VAR left:= root.left;
      BEGIN
        root.left:= left.right;
        left.right:= root;
        IF NARROW(left, NodeT).balance = 0
          THEN
            NARROW(root, NodeT).balance:= -1;
            NARROW(left, NodeT).balance:= +1;
            bal:= TRUE;
          ELSE
            NARROW(root, NodeT).balance:= 0;
            NARROW(left, NodeT).balance:= 0; (*depth changed: continue*)
        END;
        root:= left;
      END RR;

    PROCEDURE RL (VAR root: BinTreeRep.NodeT; 
                  VAR bal: BOOLEAN) =  
      (*simple rotation left*)
      VAR right:= root.right;
      BEGIN
        root.right:= right.left;
        right.left:= root;
        IF NARROW(right, NodeT).balance = 0
          THEN
            NARROW(root, NodeT).balance:= +1;
            NARROW(right, NodeT).balance:= -1;
            bal:= TRUE;
          ELSE
            NARROW(root, NodeT).balance:= 0;
            NARROW(right, NodeT).balance:= 0; (*depth changed: continue*)
        END;
        root:= right;
      END RL;

    PROCEDURE RrR (VAR root: BinTreeRep.NodeT) = 
      (*double rotation right*)
      VAR right:= root.left.right;
      BEGIN
        root.left.right:= right.left;
        right.left:= root.left;
        IF NARROW(right, NodeT).balance = -1
          THEN NARROW(root, NodeT).balance:= +1
          ELSE NARROW(root, NodeT).balance:= 0
        END;
        IF NARROW(right, NodeT).balance = +1
          THEN NARROW(root.left, NodeT).balance:= -1
          ELSE NARROW(root.left, NodeT).balance:= 0
        END;
        root.left:= right.right;
        right.right:= root;
        root:= right;
        NARROW(right, NodeT).balance:= 0;
      END RrR;

    PROCEDURE RrL (VAR root: BinTreeRep.NodeT) = 
      (*double rotation left*)
      VAR left:= root.right.left;
      BEGIN
        root.right.left:= left.right;
        left.right:= root.right;
        IF NARROW(left, NodeT).balance = +1
          THEN NARROW(root, NodeT).balance:= -1
          ELSE NARROW(root, NodeT).balance:= 0
        END;
        IF NARROW(left, NodeT).balance = -1
          THEN NARROW(root.right, NodeT).balance:= +1
          ELSE NARROW(root.right, NodeT).balance:= 0
        END;
        root.right:= left.left;
        left.left:= root;
        root:= left;
        NARROW(left, NodeT).balance:= 0;
      END RrL;

      PROCEDURE BalanceLeft(VAR root: BinTreeRep.NodeT;
                            VAR bal: BOOLEAN) =
        BEGIN
          WITH done = NARROW(root, NodeT).balance DO
            CASE done OF
            |-1=> done:= 0;                 (*new depth: continue*)
            | 0=> done:= 1; bal:= TRUE;     (*balanced ->ok*)
            |+1=>                           (*balancing needed*)
                IF NARROW(root.right, NodeT).balance >= 0
                  THEN RL(root, bal)
                  ELSE RrL(root)
                END
            END (*CASE*)
          END (*WITH*)
        END BalanceLeft;

      PROCEDURE BalanceRight(VAR root: BinTreeRep.NodeT;
                             VAR bal: BOOLEAN) =
        BEGIN
          WITH done = NARROW(root, NodeT).balance DO
            CASE done OF
            |+1=> done:= 0;                 (*new depth: continue*)
            | 0=> done:= -1; bal:= TRUE;    (*balanced ->ok*)
            |-1=>                           (*balancing needed*)
                IF NARROW(root.left, NodeT).balance <= 0
                  THEN RR(root, bal)
                  ELSE RrR(root)
                END
            END (*CASE*)
          END (*WITH*)
        END BalanceRight;

      PROCEDURE DeleteSmallest(VAR root: BinTreeRep.NodeT;
                               VAR bal: BOOLEAN): REFANY =
        VAR deleted: REFANY;
        BEGIN
          IF root.left = NIL
            THEN
              deleted:= root.info;
              root:= root.right;
              RETURN deleted;
            ELSE
              deleted:= DeleteSmallest(root.left, bal);
              IF NOT bal THEN BalanceLeft(root, bal) END;
              RETURN deleted;
          END;
        END DeleteSmallest;

      PROCEDURE Delete(VAR root: BinTreeRep.NodeT; elm: REFANY;
                       VAR bal: BOOLEAN): REFANY =
        VAR deleted: REFANY;
        BEGIN
          IF root = NIL
            THEN RETURN NIL

            ELSIF tree.compare(root.info, elm)>0 THEN
              deleted:= Delete(root.left, elm, bal);
              IF deleted # NIL
                THEN
                  IF NOT bal THEN BalanceLeft(root, bal) END;
                  RETURN deleted;
                ELSE RETURN NIL;
              END

            ELSIF tree.compare(root.info, elm)<0 THEN
              deleted:= Delete(root.right, elm, bal);
              IF deleted # NIL
                THEN
                  IF NOT bal THEN BalanceRight(root, bal) END;
                  RETURN deleted;
                ELSE RETURN NIL;
              END

            ELSE
              deleted:= root.info;
              IF root.left = NIL
                THEN
                  root:= root.right;
                ELSIF root.right = NIL THEN
                  root:= root.left;
                ELSE
                  root.info:= DeleteSmallest(root.right, bal);
                  IF NOT bal THEN BalanceRight(root, bal) END;
              END;
              RETURN deleted;
          END;
        END Delete;

    VAR balanced:= FALSE;
    BEGIN (*Delete*)
      RETURN Delete(tree.root, e, balanced)
    END Delete;

BEGIN
END AVLTree.

Deletion from AVL Trees

We have seen that AVL trees are tex2html_wrap_inline189 for insertion and query.

But what about deletion?

Don't ask! Actually, you can rebalance an AVL tree in tex2html_wrap_inline191 but it is more complicated than insertion.

We will later study B-trees, where deletion is simpler, so don't worry about the details of deletions form AVL trees.




next up previous
Next: About this document Up: My Home Page

Steve Skiena
Tue Nov 25 14:04:19 EST 1997