平衡二叉查找树(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树定义
1 2 3 4 5 6 7 8 9 10 11 | struct AvlNode; typedef struct AvlNode *Position; typedef struct AvlNode *AvlTree; struct AvlNode { ElementType Element; AvlTree Left; AvlTree Right; int Height; }; |
两个辅助函数
1 2 3 4 5 6 7 8 9 10 11 12 | 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; } |
基本操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | 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的左子树即可。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | /* 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型对称,原理类似
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | /* 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的根节点,执行一次右右旋转即可。
1 2 3 4 5 6 7 8 9 10 11 12 13 | /* 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型双旋转对称,原理类似
1 2 3 4 5 6 7 8 9 10 11 12 13 | /* 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的,如果有,则要做相应的旋转。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | 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、结点删除
结点删除包括四种情况:
- 左右子树都非空
- 左子树为空,右子树非空
- 右子树为空,左子树非空
- 左右子树都为空
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 | 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; } |
完整代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 | 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; } |
┆ 凉 ┆ 暖 ┆ 降 ┆ 等 ┆ 幸 ┆ 我 ┆ 我 ┆ 里 ┆ 将 ┆ ┆ 可 ┆ 有 ┆ 谦 ┆ 戮 ┆ 那 ┆ ┆ 大 ┆ ┆ 始 ┆ 然 ┆
┆ 薄 ┆ 一 ┆ 临 ┆ 你 ┆ 的 ┆ 还 ┆ 没 ┆ ┆ 来 ┆ ┆ 是 ┆ 来 ┆ 逊 ┆ 没 ┆ 些 ┆ ┆ 雁 ┆ ┆ 终 ┆ 而 ┆
┆ ┆ 暖 ┆ ┆ 如 ┆ 地 ┆ 站 ┆ 有 ┆ ┆ 也 ┆ ┆ 我 ┆ ┆ 的 ┆ 有 ┆ 精 ┆ ┆ 也 ┆ ┆ 没 ┆ 你 ┆
┆ ┆ 这 ┆ ┆ 试 ┆ 方 ┆ 在 ┆ 逃 ┆ ┆ 会 ┆ ┆ 在 ┆ ┆ 清 ┆ 来 ┆ 准 ┆ ┆ 没 ┆ ┆ 有 ┆ 没 ┆
┆ ┆ 生 ┆ ┆ 探 ┆ ┆ 最 ┆ 避 ┆ ┆ 在 ┆ ┆ 这 ┆ ┆ 晨 ┆ ┆ 的 ┆ ┆ 有 ┆ ┆ 来 ┆ 有 ┆
┆ ┆ 之 ┆ ┆ 般 ┆ ┆ 不 ┆ ┆ ┆ 这 ┆ ┆ 里 ┆ ┆ 没 ┆ ┆ 杀 ┆ ┆ 来 ┆ ┆ ┆ 来 ┆
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)