平衡二叉查找树(AVL)的理解与实现
AVL树的介绍
平衡二叉树,又称AVL(Adelson-Velskii和Landis)树,是带有平衡条件的二叉查找树。这个平衡条件必须要容易保持,而且它必须保证树的深度是 O(log N)。一棵AVL树是其每个节点的左子树和右子树的高度最多差1的二叉查找树( 空树的高度定义为 -1 )。查找、插入和删除在平均和最坏情况下都是 O(log n)。增加和删除可能需要通过一次或多次树旋转来重新平衡这个树。可以证明,大致上讲,一个AVL树的高度最多为 1.44log( N + 2 ) - 1.328,但是实际上的高度只比 log N 稍微多一些。
平衡因子( Balance Factor,简称BF) : BF(T) = hL - hR( 有时相反 ),其中 hL 和 hR 分别为 T 的左、右子树的高度。一棵平衡二叉树其 | BF | <= 1;带有平衡因子 -2 或 2 的节点被认为是不平衡的,并需要重新平衡这个树。平衡因子可以直接存储在每个节点中,或从可能存储在节点中的子树高度计算出来。

非AVL树的例子 同一个树在平衡之后的样子
AVL节点数计算
高度为h的AVL树,节点数N最多
; 最少
( 其中
)
最少节点数n如以斐波那契数列可以用数学归纳法证明:
=
- 1 (
是Fibonacci polynomial)。
即:
= 0 (表示AVL Tree高度为0的节点总数)
= 1 (表示AVL Tree高度为1的节点总数)
= 2 (表示AVL Tree高度为2的节点总数)
=
+
+ 1 (表示AVL Tree高度为h的节点总数)
换句话说,当节点数为N时,高度h最多为
。

AVL树的操作
AVL树的基本操作一般涉及运作同在不平衡的二叉查找树所运作的同样的算法。但是要进行预先或随后做一次或多次所谓的"AVL旋转"。
以下图表以四列表示四种情况,每行表示在该种情况下要进行的操作。在左左和右右的情况下,只需要进行一次旋转操作;在左右和右左的情况下,需要进行两次旋转操作。

实现描述:
假设平衡因子是左子树的高度减去右子树的高度所得到的值,又假设由于在二叉排序树上插入节点而失去平衡的最小子树根节点的指针为a(即a是离插入点最近,且平衡因子绝对值超过1的祖先节点),则失去平衡后进行的规律可归纳为下列四种情况:
- 单向右旋平衡处理LL:由于在*a的左子树根节点的左子树上插入节点,*a的平衡因子由1增至2,致使以*a为根的子树失去平衡,则需进行一次右旋转操作;
- 单向左旋平衡处理RR:由于在*a的右子树根节点的右子树上插入节点,*a的平衡因子由-1变为-2,致使以*a为根的子树失去平衡,则需进行一次左旋转操作;
- 双向旋转(先左后右)平衡处理LR:由于在*a的左子树根节点的右子树上插入节点,*a的平衡因子由1增至2,致使以*a为根的子树失去平衡,则需进行两次旋转(先左旋后右旋)操作。
- 双向旋转(先右后左)平衡处理RL:由于在*a的右子树根节点的左子树上插入节点,*a的平衡因子由-1变为-2,致使以*a为根的子树失去平衡,则需进行两次旋转(先右旋后左旋)操作。
在平衡的二叉排序树BBST (Balancing Binary Search Tree)上插入一个新的数据元素e的递归算法可描述如下:
- 若BBST为空树,则插入一个数据元素为e的新节点作为BBST的根节点,树的深度增1;
- 若e的关键字和BBST的根节点的关键字相等,则不进行;
- 若e的关键字小于BBST的根节点的关键字,而且在BBST的左子树中不存在和e有相同关键字的节点,则将e插入在BBST的左子树上,并且当插入之后的左子树深度增加(+1)时,分别就下列不同情况处理之:若e的关键字大于BBST的根节点的关键字,而且在BBST的右子树中不存在和e有相同关键字的节点,则将e插入在BBST的右子树上,并且当插入之后的右子树深度增加(+1)时,分别就不同情况处理之。
- BBST的根节点的平衡因子为-1(右子树的深度大于左子树的深度,则将根节点的平衡因子更改为0,BBST的深度不变;
- BBST的根节点的平衡因子为0(左、右子树的深度相等):则将根节点的平衡因子更改为1,BBST的深度增1;
- BBST的根节点的平衡因子为1(左子树的深度大于右子树的深度):则若BBST的左子树根节点的平衡因子为1:则需进行单向右旋平衡处理,并且在右旋处理之后,将根节点和其右子树根节点的平衡因子更改为0,树的深度不变;
- 若e的关键字大于BBST的根节点的关键字,而且在BBST的右子树中不存在和e有相同关键字的节点,则将e插入在BBST的右子树上,并且当插入之后的右子树深度增加(+1)时,分别就不同情况处理之。
实现代码:
Avl树定义
struct AvlNode;
typedef struct AvlNode *Position;
typedef struct AvlNode *AvlTree;
struct AvlNode
{
ElementType Element;
AvlTree Left;
AvlTree Right;
int Height;
};
两个辅助函数
static int Height( Position P )
{
if( P == NULL )
return -1;
else
return P->Height;
}
static int Max( int Lhs, int Rhs )
{
return Lhs > Rhs ? Lhs : Rhs;
}
基本操作
Position Find( ElementType X, AvlTree T )
{
if( T == NULL )
return NULL;
if( X < T->Element )
return Find( X, T->Left );
else if( X > T->Element )
return Find( X, T->Right );
else
return T;
}
Position FindMin( AvlTree T )
{
if( T == NULL )
return NULL;
else if( T->Left == NULL )
return T;
else
return FindMin( T->Left );
}
Position FindMax( AvlTree T )
{
if( T != NULL )
while( T->Right != NULL )
T = T->Right;
return T;
}
四种情况下的旋转
1、LL型(单次右旋转)
k2的左子树比右子树高2,所以不平衡,我们需要把k1提到k2的位置,然后k2下降到k1的右子树,最后还有把k1的右子树放到k2的左子树即可。

/* This function can be called only if K2 has a left child */
/* Perform a rotate between a node (K2) and its left child */
/* Update heights, then return new root */
//LL型旋转(单次右旋)
static Position SingleRotateWithLeft( Position K2 )
{
Position K1;
K1 = K2->Left; //找到 K2 左子树
K2->Left = K1->Right; //将 K1 右子树移到 K2 的左子树
K1->Right = K2; //建立 K1 和 K2 的关系
K2->Height = Max( Height( K2->Left ), Height( K2->Right ) ) + 1;
K1->Height = Max( Height( K1->Left ), K2->Height ) + 1;
return K1; /* New root */
}
2、RR型(单次右旋转)
与LL型对称,原理类似

/* This function can be called only if K1 has a right child */
/* Perform a rotate between a node (K1) and its right child */
/* Update heights, then return new root */
//RR型旋转(单次左旋)
//RR型与LL型为对称,原理类似
static Position SingleRotateWithRight( Position K1 )
{
Position K2;
K2 = K1->Right;
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; /* New root */
}
3、LR型双旋转(单次左旋后右旋)
先以K3的左子树为根节点执行一次左左旋转,然后以k3的根节点,执行一次右右旋转即可。

/* This function can be called only if K3 has a left */
/* child and K3's left child has a right child */
/* Do the left-right double rotation */
/* Update heights, then return new root */
//LR型双旋转( 单次左旋(RR型)后单次右旋(LL型))
static Position DoubleRotateWithLeft( Position K3 )
{
/* Rotate between K1 and K2 */
K3->Left = SingleRotateWithRight( K3->Left );
/* Rotate between K3 and K2 */
return SingleRotateWithLeft( K3 );
}
4、RL型双旋转(单次右旋后单次左旋)
与LR型双旋转对称,原理类似

/* This function can be called only if K1 has a right */
/* child and K1's right child has a left child */
/* Do the right-left double rotation */
/* Update heights, then return new root */
//RL型双旋转 ( 单次右旋(LL型) 后单次左旋(RR型) )
static Position DoubleRotateWithRight( Position K1 )
{
/* Rotate between K3 and K2 */
K1->Right = SingleRotateWithLeft( K1->Right );
/* Rotate between K1 and K2 */
return SingleRotateWithRight( K1 );
}
5、结点插入
AVL树进行插入操作时,会破坏二叉树的平衡性,所以每当插入一个数据时,都要从插入点往上,一步一步检测出是否出现平衡因子大于1的,如果有,则要做相应的旋转。
AvlTree Insert( ElementType X, AvlTree T )
{
if( T == NULL )
{
/* Create and return a one-node tree */
T = malloc( sizeof( struct AvlNode ) );
if( T == NULL )
FatalError( "Out of space!!!" );
else
{
T->Element = X;
T->Height = 0;
T->Left = T->Right = NULL;
}
}
else if( X < T->Element ) //插入值比当前结点值小,往左子树插入
{
T->Left = Insert( X, T->Left ); //递归插入
if( Height( T->Left ) - Height( T->Right ) == 2 ) //判断子树是否平衡
if( X < T->Left->Element ) //比当前结点左子树的值小,即为左左情形。
T = SingleRotateWithLeft( T ); //LL旋转(右单旋转)
else //否则为左右情形
T = DoubleRotateWithLeft( T ); //LR旋转
}
else if( X > T->Element ) //插入值比当前结点值大,往右子树插入
{
T->Right = Insert( X, T->Right ); //递归插入
if( Height( T->Right ) - Height( T->Left ) == 2 ) //判断子树是否平衡
if( X > T->Right->Element ) //比当前结点右子树大,即为右右情形
T = SingleRotateWithRight( T ); //RR旋转(左单旋转)
else //否则为右左情形
T = DoubleRotateWithRight( T ); //RL旋转
}
/* Else X is in the tree already; we'll do nothing */
T->Height = Max( Height( T->Left ), Height( T->Right ) ) + 1;
return T;
}
6、结点删除
结点删除包括四种情况:
- 左右子树都非空
- 左子树为空,右子树非空
- 右子树为空,左子树非空
- 左右子树都为空
AvlTree Delete(ElementType X,AvlTree T)
{
if (T == NULL) //空树直接返回
return NULL;
if (x < T->Element) //删除值小于当前节点,说明删除节点在当前节点左侧
{
T->Left = Delete(X,T->Left);
if (Height(T->Right) - Height(T->Left) == 2)
{
if (Height(T->Right->Left) > Height(T->Right->Right))
T = DoubleRotateRL(T);
else
T = SingleRotateRight(T);
}
}
else if (X > T->Element) //删除节点在当前节点右侧
{
T->Right = Delete(X,T->Right);
if (Height(T->Left) - Height(T->Right) == 2)
{
if (Height(T->Left->Right) > Height(T->Left->Left))
T = DoubleRotateLR(T);
else
T = SingleRotateLeft(T);
}
}
else //找到删除节点
{
if (T->Right) //右子树不为空的情况
{
AvlTree tmp = T->Right;
while (tmp->Left != NULL) tmp = tmp->Left; //找到要删除的结点的右子树的左子树最小值,以便替要删除的结点
T->Element = tmp->Element;
T->height = tmp->height;
T->Right = Delete(tmp->Element,T->Right); //递归删除
}
else
{
//右子树为空的情况,free节点,返回被删除节点的左节点
//这也是真正删除节点的地方
AvlTree tmp=T;
T = T->Left;
free(emp);
return T;
}
}
//每次删除之后,都要更新节点的高度
T->height = Max(Height(T->Left),Height(T->Right)) + 1;
return T;
}
完整代码
struct AvlNode;
typedef struct AvlNode *Position;
typedef struct AvlNode *AvlTree;
struct AvlNode
{
ElementType Element;
AvlTree Left;
AvlTree Right;
int Height;
};
AvlTree MakeEmpty( AvlTree T )
{
if( T != NULL )
{
MakeEmpty( T->Left );
MakeEmpty( T->Right );
free( T );
}
return NULL;
}
Position Find( ElementType X, AvlTree T )
{
if( T == NULL )
return NULL;
if( X < T->Element )
return Find( X, T->Left );
else if( X > T->Element )
return Find( X, T->Right );
else
return T;
}
Position FindMin( AvlTree T )
{
if( T == NULL )
return NULL;
else if( T->Left == NULL )
return T;
else
return FindMin( T->Left );
}
Position FindMax( AvlTree T )
{
if( T != NULL )
while( T->Right != NULL )
T = T->Right;
return T;
}
static int Height( Position P )
{
if( P == NULL )
return -1;
else
return P->Height;
}
static int Max( int Lhs, int Rhs )
{
return Lhs > Rhs ? Lhs : Rhs;
}
/* This function can be called only if K2 has a left child */
/* Perform a rotate between a node (K2) and its left child */
/* Update heights, then return new root */
//LL型旋转(单次右旋)
static Position SingleRotateWithLeft( Position K2 )
{
Position K1;
K1 = K2->Left; //找到 K2 左子树
K2->Left = K1->Right; //将 K1 右子树移到 K2 的左子树
K1->Right = K2; //建立 K1 和 K2 的关系
K2->Height = Max( Height( K2->Left ), Height( K2->Right ) ) + 1;
K1->Height = Max( Height( K1->Left ), K2->Height ) + 1;
return K1; /* New root */
}
/* This function can be called only if K1 has a right child */
/* Perform a rotate between a node (K1) and its right child */
/* Update heights, then return new root */
//RR型旋转(单次左旋)
//RR型与LL型为对称,原理类似
static Position SingleRotateWithRight( Position K1 )
{
Position K2;
K2 = K1->Right;
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; /* New root */
}
/* This function can be called only if K3 has a left */
/* child and K3's left child has a right child */
/* Do the left-right double rotation */
/* Update heights, then return new root */
//LR型双旋转( 单次左旋(RR型)后单次右旋(LL型))
static Position DoubleRotateWithLeft( Position K3 )
{
/* Rotate between K1 and K2 */
K3->Left = SingleRotateWithRight( K3->Left );
/* Rotate between K3 and K2 */
return SingleRotateWithLeft( K3 );
}
/* This function can be called only if K1 has a right */
/* child and K1's right child has a left child */
/* Do the right-left double rotation */
/* Update heights, then return new root */
//RL型双旋转 ( 单次右旋(LL型) 后单次左旋(RR型) )
static Position DoubleRotateWithRight( Position K1 )
{
/* Rotate between K3 and K2 */
K1->Right = SingleRotateWithLeft( K1->Right );
/* Rotate between K1 and K2 */
return SingleRotateWithRight( K1 );
}
AvlTree Insert( ElementType X, AvlTree T )
{
if( T == NULL )
{
/* Create and return a one-node tree */
T = malloc( sizeof( struct AvlNode ) );
if( T == NULL )
FatalError( "Out of space!!!" );
else
{
T->Element = X;
T->Height = 0;
T->Left = T->Right = NULL;
}
}
else if( X < T->Element ) //插入值比当前结点值小,往左子树插入
{
T->Left = Insert( X, T->Left ); //递归插入
if( Height( T->Left ) - Height( T->Right ) == 2 ) //判断子树是否平衡
if( X < T->Left->Element ) //比当前结点左子树的值小,即为左左情形。
T = SingleRotateWithLeft( T ); //LL旋转(右单旋转)
else //否则为左右情形
T = DoubleRotateWithLeft( T ); //LR旋转
}
else if( X > T->Element ) //插入值比当前结点值大,往右子树插入
{
T->Right = Insert( X, T->Right ); //递归插入
if( Height( T->Right ) - Height( T->Left ) == 2 ) //判断子树是否平衡
if( X > T->Right->Element ) //比当前结点右子树大,即为右右情形
T = SingleRotateWithRight( T ); //RR旋转(左单旋转)
else //否则为右左情形
T = DoubleRotateWithRight( T ); //RL旋转
}
/* Else X is in the tree already; we'll do nothing */
T->Height = Max( Height( T->Left ), Height( T->Right ) ) + 1;
return T;
}
AvlTree Delete(ElementType X,AvlTree T)
{
if (T == NULL) //空树直接返回
return NULL;
if (x < T->Element) //删除值小于当前节点,说明删除节点在当前节点左侧
{
T->Left = Delete(X,T->Left);
if (Height(T->Right) - Height(T->Left) == 2)
{
if (Height(T->Right->Left) > Height(T->Right->Right))
T = DoubleRotateRL(T);
else
T = SingleRotateRight(T);
}
}
else if (X > T->Element) //删除节点在当前节点右侧
{
T->Right = Delete(X,T->Right);
if (Height(T->Left) - Height(T->Right) == 2)
{
if (Height(T->Left->Right) > Height(T->Left->Left))
T = DoubleRotateLR(T);
else
T = SingleRotateLeft(T);
}
}
else //找到删除节点
{
if (T->Right) //右子树不为空的情况
{
AvlTree tmp = T->Right;
while (tmp->Left != NULL) tmp = tmp->Left; //找到要删除的结点的右子树的左子树最小值,以便替要删除的结点
T->Element = tmp->Element;
T->height = tmp->height;
T->Right = Delete(tmp->Element,T->Right); //递归删除
}
else
{
//右子树为空的情况,free节点,返回被删除节点的左节点
//这也是真正删除节点的地方
AvlTree tmp=T;
T = T->Left;
free(emp);
return T;
}
}
//每次删除之后,都要更新节点的高度
T->height = Max(Height(T->Left),Height(T->Right)) + 1;
return T;
}
平衡二叉查找树(AVL)的理解与实现的更多相关文章
- 006-数据结构-树形结构-二叉树、二叉查找树、平衡二叉查找树-AVL树
一.概述 树其实就是不包含回路的连通无向图.树其实是范畴更广的图的特例. 树是一种数据结构,它是由n(n>=1)个有限节点组成一个具有层次关系的集合. 1.1.树的特性: 每个结点有零个或多个子 ...
- 【查找结构3】平衡二叉查找树 [AVL]
在上一个专题中,我们在谈论二叉查找树的效率的时候.不同结构的二叉查找树,查找效率有很大的不同(单支树结构的查找效率退化成了顺序查找).如何解决这个问题呢?关键在于如何最大限度的减小树的深度.正是基于这 ...
- 平衡二叉查找树 AVL 的实现
不同结构的二叉查找树,查找效率有很大的不同(单支树结构的查找效率退化成了顺序查找).如何解决这个问题呢?关键在于如何最大限度的减小树的深度.正是基于这个想法,平衡二叉树出现了. 平衡二叉树的定义 (A ...
- 面试题:什么叫平衡二叉查找树--AVL树
查找.插入和删除在平均和最坏情况下都是O(log n) 增加和删除可能需要通过一次或多次树旋转来重新平衡这个树 节点的平衡因子是它的左子树的高度减去它的右子树的高度.带有平衡因子 1.0 或 -1 的 ...
- 平衡树初阶——AVL平衡二叉查找树+三大平衡树(Treap + Splay + SBT)模板【超详解】
平衡树初阶——AVL平衡二叉查找树 一.什么是二叉树 1. 什么是树. 计算机科学里面的树本质是一个树状图.树首先是一个有向无环图,由根节点指向子结点.但是不严格的说,我们也研究无向树.所谓无向树就是 ...
- AVL树(平衡二叉查找树)
首先要说AVL树,我们就必须先说二叉查找树,先介绍二叉查找树的一些特性,然后我们再来说平衡树的一些特性,结合这些特性,然后来介绍AVL树. 一.二叉查找树 1.二叉树查找树的相关特征定义 二叉树查找树 ...
- 数据结构-自平衡二叉查找树(AVL)详解
介绍: 在计算机科学中,AVL树是最先发明的自平衡二叉查找树. 在AVL树中任何节点的两个子树的高度最大差别为一,所以它也被称为高度平衡树. 查找.插入和删除在平均和最坏情况下都是O(log n).增 ...
- 算法学习 - 平衡二叉查找树实现(AVL树)
平衡二叉查找树 平衡二叉查找树是非常早出现的平衡树,由于全部子树的高度差不超过1,所以操作平均为O(logN). 平衡二叉查找树和BS树非常像,插入和删除操作也基本一样.可是每一个节点多了一个高度的信 ...
- 树的平衡之AVL树——错过文末你会后悔,信我
学习数据结构应该是一个循序渐进的过程: 当我们学习数组时,我们要体会数组的优点:仅仅通过下标就可以访问我们要找的元素(便于查找). 此时,我们思考:假如我要在第一个元素前插入一个新元素?采用数组需要挪 ...
- AVL树理解
AVL树理解 简介 我们知道,AVL树也是平衡树中的一种,是自带平衡条件的二叉树,始终都在维护树的高度,保持着树的高度为logN,同时把插入.查找.删除一个结点的时间复杂度的最好和最坏情况都维持在O( ...
随机推荐
- asp.net mvc 之旅—— 第三站 路由模板中强大的自定义IRouteConstraint约束
我们在写mvc的时候,经常会配置各种url模板,比如controller,action,id 组合模式,其实呢,我们还可以对这三个参数进行单独的配置,采用的方式自然 就是MapRoute中的const ...
- MongoDB学习笔记~大叔分享批量添加—批量更新—批量删除
回到目录 说它是批量操作,就是说将集合对象一次提交到服务器,并对数据进行持久化,如果您的代码是一次一次的提交,那不算是批量操作!在之前的mongodb仓储中并没有对批量更新和批量删除进行实现,而今天在 ...
- mysql常用基本操作
mysql常用操作 查看都有哪些库 show databases; 查看某个库的表 use 库名; show tables; 查看表的字段 desc 表名; 当前是哪个用户 select user() ...
- Linux 负载监控脚本
#!/bin/bash Date=`echo $(date +%Y\-%m\-%d\ %H:%M:%S)`HostName=`hostname`IP=`ifconfig eth0 | grep &qu ...
- 跨云应用部署第一步:使用IPSEC VPN连接AWS中国版和Windows Azure中国版
随着公有云的普及,越来越多的客户将关键应用迁移到云端.但是事实证明,没有哪家云服务提供商可以提供100%的SLA,无论是例行维护还是意外中断服务,对于客户的关键应用而言,都会受到不同程度的影响.此外, ...
- 使用maven将代码到私服
在上一节中,我们讲述了maven私服搭建 ,那么本章,我们讲述如何使用maven将代码打包并上传到maven私服上. 一.maven pom.xml关键配置信息. <distributionMa ...
- UVALive 4870 Roller Coaster --01背包
题意:过山车有n个区域,一个人有两个值F,D,在每个区域有两种选择: 1.睁眼: F += f[i], D += d[i] 2.闭眼: F = F , D -= K 问在D小于等于一定限度的时 ...
- Spring基础[IOC/DI、AOP]
一.Spring作用:管理项目中各种业务Bean(service类.Dao类.Action类),实例化类,属性赋值 二.Spring IOC(Inversion of Control )控制反转,也被 ...
- mybatis缓存
mybatis缓存http://www.cnblogs.com/QQParadise/articles/5109633.htmlhttp://www.mamicode.com/info-detail- ...
- IdentityHashMap类:增加相同Key(键)内容的结果
正常Map操作中,key是不能重复的. 先看使用HashMap()操作,增加相同Key内容的结果: 先看代码: package 类集; import java.util.HashMap; import ...