平衡二叉查找树(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( ...
随机推荐
- maven工程模块化
前言 项目的模块化有利于任务分工,后期维护,易扩展,模块还可以独立成服务单独部署等: 创建packaging类型为POM的父项目 我用的maven插件是m2e,相信大部分人在eclipse装的也是m2 ...
- 归档—监控ORACLE数据库告警日志
ORACLE的告警日志里面包含许多有用的信息,尤其是一些ORACLE的ORA错误信息,所以有必要及时归档.监控数据库告警日志的ORA错误,及时提醒数据库管理员DBA处理这些错误信息,那么我们首先来看看 ...
- Linux系统查看系统是32位还是64位方法总结
这篇博客是总结.归纳查看Linux系统是32位还是64位的一些方法,很多内容来自网上网友的博客.本篇只是整理.梳理这方面的知识,方便自己忘记的时候随时查看. 方法1:getconf LONG_BIT ...
- List tuple 类型转成数组
SKlearning大部分的输入数据都是M * N数组. 然而我们从数据库或文件读取得来的通常是Python内定的类型tuple或list 它们的优势就不说了,但是直接把list或tuple构成的二维 ...
- SQL Server自动化运维系列——监控跑批Job运行状态(Power Shell)
需求描述 在我们的生产环境中,大部分情况下需要有自己的运维体制,包括自己健康状态的检测等.如果发生异常,需要提前预警的,通知形式一般为发邮件告知. 在上一篇文章中已经分析了SQL SERVER中关于邮 ...
- Apache配置
redhat6.4企业版用的centos的yum源. 下面进行apache的安装与配置. 1.yum在线安装Apache 一般不采用yum在线安装因为如果apache坏掉了,yum安装过程中会有依赖的 ...
- MSSQL复制中的发布与订阅
准备条件 1.2台服务器 2.WINDOWS SERVER 2008 64bit + 3.SQL SERVER 2008 R2 + 4.MSSQLSERVER服务与MSSQLAGENT服务正常运行中 ...
- 13、Apache中虚拟目录和目录权限配置
一.虚拟目录 之前的个人主页,为了安全起见,需要把~yanji 用户隐藏起来,这时就可以设置个 虚拟目录. 它在Apache服务器应用比较多,能够隐藏系统的真实目录,实用性非常高. 虚拟目录主要 通过 ...
- 阅读Real-Time O(1) Bilateral Filtering 一文的相关感受。
研究双边滤波有很长一段时间了,最近看了一篇Real-Time O(1) Bilateral Filtering的论文,标题很吸引人,就研读了一番,经过几天的攻读,基本已理解其思想,现将这一过程做一简单 ...
- NYOJ---540奇怪的排序
奇怪的排序 时间限制:1000 ms | 内存限制:65535 KB 难度:1 描述 最近,Dr. Kong 新设计一个机器人Bill.这台机器人很聪明,会做许多事情.惟独对自然数的理解与人类不一 ...