基本数据结构之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


    原文作者:AVL树
    原文地址: https://blog.csdn.net/luozhaowork/article/details/10296137
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞