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