基本数据结构之AVL树

AVL树即自平衡二叉查找树。在AVL树中,任何两个结点的两个子树的高度最大差别为1,所以AVL树也被称为高度平衡树。

AVL树在插入和删除和查找的时间复杂度在平均和最坏情况下都是O(log N),插入和删除需要通过1次或者多次旋转重新使树达到平衡。

怎么判断AVL树是平衡的呢?需要通过平衡因子来来判断,结点的平衡因子是是它的左子树高度减去右子树高度.平衡因子为1、0、-1的结点是平衡的。反之,则是不平衡的。

几种旋转的情况:

1:RR旋转

  初始:     c

           /

         b

  现在插入一个元素a,如下图所示, 这时,AVL已经不平衡了,经过向右旋转-->         b         ,此时,树平衡了。

             c                                                                /   \

           /                                                                a       c

         b

       /

     a

2:LL 旋转

初始:   c

           \

             b

    现在插入一个元素a,如下图所示,这是AVL已经不平衡了,经过向左旋转-->         b         ,此时,树平衡了

        c                                                                    /   \

          \                                                                c       a

            b

             \

               a

3:LR旋转

初始:   c

       /

     b

在b的右子树插入结点,需要经过两次旋转,首先将a向左旋转,然后将c向右旋转:

        c                    c                   b

      /                    /                   /   \

     b     ----->        b      ---->        a      c

       \               /

         a           a

4:RL旋转

初始: c

        \

          b

在b的左子树插入结点a,首相将a向右旋转,然后将c向左旋转。

       c                 c                       b

         \                 \                   /   \

           b  ---->          b        --->   c      a

         /                     \

        a                        a


了解了以上四种旋转方式,在接下来的AVL树的插入或阐述操作中进行平衡操作时就更容易理解了。

下面具体讲解下AVL树的实现。

//

  
  
/*
AVL with c implentation
*/
typedef enum { LEFT = 0 , RIGHT = 1 } direction_t ;
#define MAX(a,b) ( (a) > (b) ? (a) : (b) )
#define MIN(a,b) ( (a) < (b) ? (a) : (b) )
#define OTHER_DIR(x) direction_t( 1 - (x) )
typedef struct avl_node {
   DATA_TYPE data ;
   short bal ;
   strutc avl_node * subtree [ 2 ];
} AVLnode_t , * AVLtree_t ; /* avl_node */
const AVLtree_t * AVLnull = (( AVLtree_t ) NULL );
/*
* rotate_once() -- rotate a given node in the given direction
* to restore the balance of a tree
*/
short rotate_once ( AVLtree_t * rootp , direction_t dir )
{
   direction_t other_dir = OTHER_DIR ( dir ); /* opposite of "dir" */
   AVLtree_t old_root = * rootp ; /* copy of original root */
   short ht_unchanged ; /* true if height unchanged */
  
   /* Here we need to take into account the special case that occurs
** when a single rotation was made but the HEIGHT of the rotated
** tree did NOT change as a result of the rotation (we will need
** this later)
*/
   ht_unchanged = (( * rootp ) -> subtree [ other_dir ] -> bal ) ? FALSE : TRUE ;
  
     /* assign new root */
   * rootp = old_root -> subtree [ other_dir ];
  
     /* new-root exchanges it's "dir" subtree for it's parent */
   old_root -> subtree [ other_dir ] = ( * rootp ) -> subtree [ dir ];
   ( * rootp ) -> subtree [ dir ] = old_root ;
  
     /* update balances */
   old_root -> bal = - ( dir == LEFT ? -- (( * rootp ) -> bal ) : ++ (( * rootp ) -> bal ));
  
    return ht_unchanged ;
  
} /* rotate_once */
/* rotate_twice() -- rotate a given node in the given direction
* and then in the opposite direction
* to restore the balance of a tree
*/
void rotate_twice ( AVLtree * rootp , direction_t dir )
{
   direction_t other_dir = OTHER_DIR ( dir );
   AVLtree_t old_root = * rootp ;
   AVLtree_t old_other_dir_subtree = ( * rootp ) -> subtree [ other_dir ];
       /* assign new root */
   * rootp = old_root -> subtree [ other_dir ] -> subtree [ dir ];
       /* new-root exchanges it's "dir" subtree for it's grandparent */
   old_root -> subtree [ other_dir ] = ( * rootp ) -> subtree [ dir ];
   ( * rootp ) -> subtree [ dir ] = old_root ;
        /* new-root exchanges it's "other-dir" subtree for it's parent */
   old_other_dir_subtree -> subtree [ dir ] = ( * rootp ) -> subtree [ other_dir ];
   ( * rootp ) -> subtree [ other_dir ] = old_other_dir_subtree ;
         /* update balances */
   ( * rootp ) -> subtree [ LEFT ] -> bal = - MAX (( * rootp ) -> bal , 0 );
   ( * rootp ) -> subtree [ RIGHT ] -> bal = - MIN (( * rootp ) -> bal , 0 );
   ( * rootp ) -> bal = 0 ;
} /* rotate_twice */
  /* Balance Definitions */
enum { LEFT_HEAVY = - 1 , BALANCED = 0 , RIGHT_HEAVY = 1 };
#define LEFT_IMBALANCE(nd) ( (nd)->bal < LEFT_HEAVY )
#define RIGHT_IMBALANCE(nd) ( (nd)->bal > RIGHT_HEAVY )
  /*
* balance() -- determines and performs the sequence of rotations needed
* (if any) to restore the balance of a given tree.
*
* Returns 1 if tree height changed due to rotation; 0 otherwise
*/
   short
   balance ( AVLtree_t * rootp ) {
       short special_case = FALSE ;
       if ( LEFT_IMBALANCE ( * rootp )) { /* need a right rotation */
         if (( * rootp ) -> subtree [ LEFT ] -> bal == RIGHT_HEAVY ) {
             rotate_twice ( rootp , RIGHT ); /* double RL rotation needed */
         } else { /* single RR rotation needed */
             special_case = rotate_once ( rootp , RIGHT );
         }
       } else if ( RIGHT_IMBALANCE ( * rootp )) { /* need a left rotation */
         if (( * rootp ) -> subtree [ RIGHT ] -> bal == LEFT_HEAVY ) {
             rotate_twice ( rootp , LEFT ); /* double LR rotation needed */
         } else { /* single LL rotation needed */
             special_case = rotate_once ( rootp , LEFT );
         }
       } else {
         return HEIGHT_UNCHANGED ; /* no rotation occurred */
       }
       return ( special_case ) ? HEIGHT_UNCHANGED : HEIGHT_CHANGED ;
   } /* balance */
 
  /*
* ckalloc(size) -- allocate space; check for success
*/
   void *
   ckalloc ( unsigned size ) {
       void * ptr ;
       if (( ptr = malloc ( size )) == NULL ) {
         fprintf ( stderr , "Unable to allocate storage." );
         exit ( 1 );
       } /* if */
       return ptr ;
   } /* ckalloc */
  /*
* new_node() -- get space for a new node and its data;
* return the address of the new node
*/
   AVLtree_t
   new_node ( void * data , unsigned size ) {
       AVLtree_t root ;
       root = ( AVLtree_t ) ckalloc ( sizeof ( AVLnode ));
       root -> data = ( void * ) ckalloc ( size );
       memmove ( root -> data , data , size );
       root -> bal = BALANCED ;
       root -> subtree [ LEFT ] = root -> subtree [ RIGHT ] = AVLnull ;
       return root ;
   } /* new_node */
  /*
* free_node() -- free space for a node and its data!
* reset the node pointer to NULL
*/
   void
   free_node ( AVLtree_t * rootp ) {
       free (( void * ) * rootp );
       * rootp = AVLnull ;
   } /* free_node */
  /*
* node_type() -- determine the number of null pointers for a given
* node in an AVL tree, Returns a value of type node_t
* which is an enumeration type with the following
* values:
*
* IS_TREE -- both subtrees are non-empty
* IS_LBRANCH -- left subtree is non-empty; right is empty
* IS_RBRANCH -- right subtree is non-empty; left is empty
* IS_LEAF -- both subtrees are empty
* IS_NULL -- given tree is empty
*/
  
   typedef enum { IS_TREE , IS_LBRANCH , IS_RBRANCH , IS_LEAF , IS_NULL } node_t ;
  
   node_t
   node_type ( AVLtree_t tree ) {
       if ( tree == AVLnull ) {
         return IS_NULL ;
       } else if (( tree -> subtree [ LEFT ] != AVLnull ) &&
                  ( tree -> subtree [ RIGHT ] != AVLnull )) {
         return IS_TREE ;
       } else if ( tree -> subtree [ LEFT ] != AVLnull ) {
         return IS_LBRANCH ;
       } else if ( tree -> subtree [ RIGHT ] != AVLnull ) {
         return IS_RBRANCH ;
       } else {
         return IS_LEAF ;
       }
   } /* node_type */
  
   /*
* avl_min() -- comparator used to find the minimal element in a tree
*/
       int
       avl_min ( void * el1 , void * el2 , node_t nd_typ ) {
           if (( nd_typ == IS_RBRANCH ) || ( nd_typ == IS_LEAF )) {
             return 0 ; /* left subtree is empty -- this is the minimum */
           } else {
             return - 1 ; /* keep going left */
           }
       } /* avl_min */
      /*
* avl_max() -- comparator used to find the maximal element in a tree
*/
       int
       avl_max ( void * el1 , void * el2 , node_t nd_typ ) {
           if (( nd_typ == IS_LBRANCH ) || ( nd_typ == IS_LEAF )) {
             return 0 ; /* right subtree is empty -- this is the maximum */
           } else {
             return 1 ; /* keep going right */
           }
       } /* avl_max */
      
        /*
* avl_compare() -- compare an item with a node-item in an avl tree
*/
       int
       avl_compare ( void * el1 , void * el2 , node_t nd_typ , int ( * el_cmp )(...)) {
          if (( el_cmp == avl_min ) || ( el_cmp == avl_max )) {
            return ( * el_cmp )( el1 , el2 , nd_typ );
          } else {
            return ( * el_cmp )( el1 , el2 );
          }
       } /* avl_compare */
/*
* avl_insert() -- insert an item into the given tree
*
* PARAMETERS:
* data -- a pointer to a pointer to the data to add;
* On exit, *data is NULL if insertion succeeded,
* otherwise address of the duplicate key
* rootp -- a pointer to an AVL tree
* compar -- name of the function to compare 2 data items
*/
       short
       avl_insert ( void ** data , AVLtree_t * rootp , int ( * el_cmp )(...)) {
           short increase ;
           int cmp ;
           if ( * rootp == AVLnull ) { /* insert new node here */
             * rootp = new_node ( * data , SIZE_OF_DATA );
             * data = NULL ; /* set return value in data */
             return HEIGHT_CHANGED ;
           } /* if */
           cmp = ( * el_cmp )( * data , ( * rootp ) -> data ); /* compare data items */
           if ( cmp < 0 ) { /* insert into the left subtree */
             increase = - avl_insert ( data , & (( * rootp ) -> subtree [ LEFT ]), el_cmp );
             if ( * data != NULL ) return HEIGHT_UNCHANGED ;
           } else if ( cmp > 0 ) { /* insert into the right subtree */
             increase = avl_insert ( data , & (( * rootp ) -> subtree [ RIGHT ]), el_cmp );
             if ( * data != NULL ) return HEIGHT_UNCHANGED ;
           } else { /* data already exists */
             * data = ( * rootp ) -> data ; /* set return value in data */
             return HEIGHT_UNCHANGED ;
           }
           ( * rootp ) -> bal += increase ; /* update balance factor */
         /**********************************************************************
* re-balance if needed -- height of current tree increases only if its
* subtree height increases and the current tree needs no rotation.
**********************************************************************/
           if ( increase && ( * rootp ) -> bal ) {
             return ( 1 - balance ( rootp ));
           } else {
             return HEIGHT_UNCHANGED ;
           }
       } /* avl_insert */
/*
* avl_delete() -- delete an item from the given tree
*
* PARAMETERS:
* data -- a pointer to a pointer to the key to delete
* On exit, *data points to the deleted data item
* (or NULL if deletion failed).
* rootp -- a pointer to an AVL tree
* compar -- name of function to compare 2 data items
*/
       short
       avl_delete ( void ** data , AVLtree_t * rootp , int ( * el_cmp )(...)) {
           short decrease ;
           int cmp ;
           AVLtree_t old_root = * rootp ;
           node_t nd_typ = node_type ( * rootp );
           direction_t dir = ( nd_typ == IS_LBRANCH ) ? LEFT : RIGHT ;
           if ( * rootp == AVLnull ) { /* data not found */
             * data = NULL ; /* set return value in data */
             return HEIGHT_UNCHANGED ;
           } /* if */
              /* compare data items */
                 /* NOTE the extra parameter to compare this time */
           cmp = el_cmp ( * data , ( * rootp ) -> data , nd_typ );
           if ( cmp < 0 ) { /* delete from left subtree */
             decrease = - avl_delete ( data , & (( * rootp ) -> subtree [ LEFT ]), el_cmp );
             if ( * data == NULL ) return HEIGHT_UNCHANGED ;
           } else if ( cmp > 0 ) { /* delete from right subtree */
             decrease = avl_delete ( data , & (( * rootp ) -> subtree [ RIGHT ]), el_cmp );
             if ( * data == NULL ) return HEIGHT_UNCHANGED ;
           } else {
         /**********************************************************************
* At this point we know that if "cmp" is zero then "*rootp" points to
* the node that we need to delete. There are three cases:
*
* 1) The node is a leaf. Remove it and return.
*
* 2) The node is a branch (has only 1 child). Make "*rootp"
* (the pointer to this node) point to the child.
*
* 3) The node has two children. We swap data with the successor of
* "*rootp" (the smallest item in its right subtree) and delete
* the successor from the right subtree of "*rootp". The
* identifier "decrease" should be reset if the subtree height
* decreased due to the deletion of the successor of "rootp".
**********************************************************************/
                /* cmp == 0 */
             * data = ( * rootp ) -> data ; /* set return value in data */
             switch ( nd_typ ) { /* what kind of node are we removing? */
                case IS_LEAF :
                   free_node ( rootp ); /* free the leaf, its height */
                   return HEIGHT_CHANGED ; /* changes from 1 to 0, return 1 */
                case IS_RBRANCH : /* only child becomes new root */
                case IS_LBRANCH :
                   * rootp = ( * rootp ) -> subtree [ dir ];
                   free_node ( & old_root ); /* free the deleted node */
                   return HEIGHT_CHANGED ; /* just shortened "dir" subtree */
                case IS_TREE :
                   decrease = avl_delete ( & (( * rootp ) -> data ),
                                         & (( * rootp ) -> subtree [ RIGHT ]),
                                         avl_min );
             } /* switch */
           } /* else */
           ( * rootp ) -> bal -= decrease ; /* update balance factor */
         /**********************************************************************
* Rebalance if necessary -- the height of current tree changes if one
* of two things happens: (1) a rotation was performed which changed
* the height of the subtree (2) the subtree height decreased and now
* matches the height of its other subtree (so the current tree now
* has a zero balance when it previously did not).
**********************************************************************/
           if ( decrease && ( * rootp ) -> bal ) { /* return 1 if height */
             return balance ( rootp ); /* changed due to rotation */
           } else if ( decrease && ! ( * rootp ) -> bal ) {
                                               /* or if balance is 0 from */
             return HEIGHT_CHANGED ; /* height decrease of subtree */
           } else {
             return HEIGHT_UNCHANGED ;
           }
       } /* avl_delete */

以上代码选自:libavl




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值