基本数据结构之AVL树

来源:互联网 发布:mac迅雷一打开就闪退 编辑:程序博客网 时间:2024/06/06 01:59

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




原创粉丝点击