概述
本文主要分成两部分:
- AVL平衡二叉树介绍
- 源代码递归逻辑分析
一、AVL(Adelson-Velskii-Landis)平衡二叉查找树介绍
为了简便起见,后面我们把AVL平衡二叉查找树简称为AVL树。
什么是AVL树,一句话:一棵AVL树是每个节点的左子树和右子树的高度最多相差1的二叉查找树。
在下图中,左边是AVL树,右边不是,因为根结点的左子树的高度为2,但右子树的高度为0,两者相差大于1。
这是一种很好的二叉树,看下图,是不是很好看?,不仅好看,在结点相同的情况下,它的时间复杂度也是比较小的。
但是,当我们在进行插入或者删除操作的时候就出现了一些问题,插入或者删除一个结点后,原有的AVL树可能就不满足AVL树定义中的平衡条件了,那么怎么办呢?我们需要通过一些简单的旋转的操作来解决这个问题。下面我们就来解决这个问题。
我们先来确认几个事实,自己思考一下为什么?。
一、在插入以后,只有那些从插入点到根结点的路径上的结点的平衡可能会被改变。
二、我们沿着插入点往上寻找,找到第一个不平衡的点,只要这个点被旋转平衡,那么整棵树也就重新平衡。
三、我们把第一个不平衡的结点叫做
α
\alpha
α,容易看出,这种不平衡是由下列四种情况之一导致的。
1、对
α
\alpha
α的左儿子的左子树进行一次插入。
2、对
α
\alpha
α的左儿子的右子树进行一次插入。
3、对
α
\alpha
α的右儿子的左子树进行一次插入。
4、对
α
\alpha
α的右儿子的右子树进行一次插入。
第一种和第四种是对称的,我们只讨论第一种,第二种和第三种是对称的,我们只讨论第二种。对这两种情况,我们分别有两种方法,一种叫单旋转,一种叫双旋转
单旋转
在第一种情况下,我们把这种情况进行普适化,图示如下,图中的红色虚线表示树的各层
我们可以看到X所到达的层比Z要深两层,所以K2是不满足平衡条件的。所以需要调整,很简单,看到那两条带颜色的线条没,现在把它们的终点互换。然后拽着K1使劲地抖动,把K1抖成根结点。操作过程如下图所示。
这便是单旋转的普适思想。下面找几个例子操作一下,看看这种方法的效果。
话不多说,看图
再看图
应该说得很明白了吧,对称问题可以类推。
双旋转
首先我们来看看在第二种情况下,单旋转法能否解决。
第二种情况是什么:对
α
\alpha
α的左儿子的右子树进行一次插入。看下图
采用单旋转法试试看,看下图
嘿嘿?,是不是相当于变到了第三种情况,并没有让它恢复平衡,那怎么办呢,下面我们来介绍双旋转法。
首先是和单旋转法一样:普适性原理介绍。下面是第二种情况的普适化。
那么具体的操作步骤是什么样呢?如果你前面的单旋转理解得很好的话,那么这里应该很容易就能理解,看下图
就是一个左旋-右旋的操作,对称地,有右旋-左旋操作。
二、源代码递归逻辑分析
是时候展现真正的技术了,下面我们就要用代码来实现我们上面的思想。
头文件
#ifndef AVLTREE_H_INCLUDED
#define AVLTREE_H_INCLUDED
typedef struct Avl_Tree{
int data;
int height;//New member variable used to save height of the Node
struct Avl_Tree *left;
struct Avl_Tree *right;
}Avl_Tree;
Avl_Tree *Insert(Avl_Tree *T, int X);
static Avl_Tree *Single_Rotate_With_Left(Avl_Tree *K2);
static Avl_Tree *Single_Rotate_With_Right(Avl_Tree *K1);
static Avl_Tree *Double_Rotate_With_Left(Avl_Tree *K3);
static Avl_Tree *Double_Rotate_With_Right(Avl_Tree *K3);
int max(int a,int b);
#endif // AVLTREE_H_INCLUDED
返回结点高度
static int Height(Avl_Tree *T)
{
if(T == NULL)
return -1;
else
return T->height;
}
在左外插入,再来回顾一下那张图
static Avl_Tree *Single_Rotate_With_Left(Avl_Tree *K2)
{
Avl_Tree *K1;
K1 = K2->left;
K2->left = K1->right;
K1->right = K2;
K2->height = max(Height(K2->left),Height(K2->right)) + 1;
K1->height = max(Height(K1->left), K2->height) + 1;
return K1;
}
在右外插入,对称地进行。
代码如下
static Avl_Tree *Single_Rotate_With_Right(Avl_Tree *K1)
{
Avl_Tree *K2;
K2 = K1->left;
K1->right = K2->left;
K2->left = K1;
K1->height = max(Height(K1->left), Height(K1->right)) + 1;
K2->height = max(Height(K2->right), K1->height) + 1;
return K2;
}
在左内插入,再来看一下图
代码如下
static Avl_Tree *Double_Rotate_With_Left(Avl_Tree *K3)
{
K3->left = Single_Rotate_With_Right(K3->left);
return Single_Rotate_With_Left(K3);
}
在右内插入,对称地进行。
代码如下
static Avl_Tree *Double_Rotate_With_Right(Avl_Tree *K1)
{
K1->right = Single_Rotate_With_Left(K1->right);
return Single_Rotate_With_Right(K1);
}
插入元素
Avl_Tree *Insert(Avl_Tree *T, int X)
{
if(T == NULL)
{
T = (Avl_Tree *)malloc(sizeof(Avl_Tree));
if(T == NULL)
{
printf("Out of space !!");
exit(1);
}
else
{
T->data = X;
T->height = 0;
T->left = T->right = NULL;
}
}
else if(X < T->data)
{
T->left = Insert(T->left, X);
if(Height(T->left) - Height(T->right) == 2)
{
if(X < T->left->data)
T = Single_Rotate_With_Left(T);
else
T = Double_Rotate_With_Right(T);
}
}
else if(X > T->data)
{
T->right = Insert(X, T->right);
if(Height(T->right) - Height(T->right) == 2)
{
if(X > T->right->data)
T = Single_Rotate_With_Left(T);
else
T = Double_Rotate_With_Left(T);
}
}
T->height = max(Height(T->left), Height(T->right)) + 1;
return T;
}