标签:特性 har node 代码 alt 超过 时间复杂度 stdio.h 关键字
文字描述
平衡二叉树(Balanced Binary Tree或Height-Balanced Tree)
因为是俄罗斯数学家G.M.Adel’son-Vel’skii和E.M.Landis在1962年提出来的,所以又称AVL树。它或者是一颗空树,或者是具有下列性质的二叉树:它的左子树和右子树都是平衡二叉树,且左子树和右子树的深度之差的绝对值不超过1。若将二叉树上结点的平衡因子BF(Balanced Factor)定义为该结点的左子树的深度减去它的右子树的深度,则平衡二叉树上所有结点的平衡因子只可能是-1,0和1。只要二叉树上有一个结点的平衡因子的绝对值大于1,则该二叉树就是不平衡的。
那么如何使二叉排序树成为平衡树呢?即在一颗二叉排序树中因插入一个结点后失去平衡的话,怎么调整才能使之重新平衡呢?
一般情况下,假设由于在二叉排序树上插入结点而失去平衡的最小子树根结点的指针a(即a是离插入结点最近,且平衡因子绝对值超过1的祖先结点),则失去平衡后进行调整的规律可归纳为下面4中情况:
(1)单向右旋平衡处理,图9.13(a)所示:在*a的左子树根结点的左子树上插入结点后,*a的平衡因子由1增至2,致使以*a为根的子树失去平衡,则需进行一次向右的顺时针旋转操作。
(2)双向旋转(先左后右),图9.13(b)所示:在*a的左子树根结点的右子树上插入结点后,*a的平衡因子由1增至2,致使以*a为根的子树失去平衡,则需进行两次旋转(先左旋后右旋)操作。
(3)单向左旋平衡处理,图9.13(c)所示:在*a的右子树根结点的右子树上插入结点后,*a的平衡因子由-1变为-2,致使以*a为根的子树失去平衡,则需进行一次向左的逆时针旋转操作。
(4)双向旋转(先右后左),图9.13(d)所示:在*a的右子树根结点的左子树上插入结点后,*a的平衡因子由-1变为-2,致使以*a为根的子树失去平衡,则需进行两次选择(先右旋后左旋)
上诉4种情况中,(1)和(3)对称,(2)和(4)对称。它们旋转后依然能保持二叉排序树的特性且由不平衡变为平衡。可以用二叉排序树的特性(”对于二叉排序树,中序遍历所得关键字序列自小至大有序”)证明之。
示意图
算法分析
在平衡二叉排序树上查找的时间复杂度为logn, 不会出现最差的情况。
代码实现
1 //./a.out 45 12 53 3 37 100 24 61 90 78 2 //./a.out 45 12 53 100 61 3 //测试 4 #include <stdio.h> 5 #include <stdlib.h> 6 #include <string.h> 7 8 #define DEBUG 9 #define TRUE 1 10 #define FALSE 0 11 #define LH +1 //左高 12 #define EH 0 //等高 13 #define RH -1 //右高 14 #define EQ(a,b) ((a)==(b)) 15 #define LT(a,b) ((a)< (b)) 16 #define LQ(a,b) ((a)<=(b)) 17 #define LCHILD 1 18 #define RCHILD 2 19 20 typedef int ElemType; 21 typedef int Boolean; 22 //平衡二叉树采用二叉链表作为存储结构 23 typedef struct BSTNode{ 24 ElemType data; 25 //结点的平衡因子 26 int bf; 27 //左,右孩子指针 28 struct BSTNode *lchild, *rchild; 29 }BSTNode, *BSTree; 30 31 /*右旋平衡处理算法 32 * 33 *对以*p为根的二叉排序树作右旋处理,处理之后p指向新的树根结点,即旋转之前的左子树的根结点。f始终指向*p的父亲结点 34 *提示:建议结合图9.13(a)看,此处*p, lc相当于图中A、B结点。 35 */ 36 void R_Rotate(BSTree *p, BSTree f){ 37 //*p是其父亲结点f的左孩子结点还是右孩子结点? 38 int flag = -1; 39 if(f && (f->lchild == (*p))){ 40 //*p是f的左孩子结点 41 flag = LCHILD; 42 } 43 if(f && (f->rchild == (*p))){ 44 //*p是f的右孩子结点 45 flag = RCHILD; 46 } 47 48 //lc指向*p的左子树根结点 49 BSTNode *lc = (BSTNode*)(*p)->lchild; 50 //lc的右子树挂接为*p的左子树 51 (*p)->lchild = lc->rchild; 52 //p指向新的根结点 53 lc->rchild = *p; 54 *p = lc; 55 56 //更新父亲结点f的孩子结点指针 57 if(f && (flag==LCHILD)){ 58 f->lchild = *p; 59 } 60 if(f && (flag==RCHILD)){ 61 f->rchild = *p; 62 } 63 } 64 65 /*左旋平衡处理算法 66 * 67 *提示:和右旋平衡算法是对称的,建议结合图9.13(c)看,此处*p,rc相当图图中的A、B结点。 68 */ 69 void L_Rotate(BSTree *p, BSTree f){ 70 int flag = -1; 71 if(f && (f->lchild == (*p))){ 72 flag = LCHILD; 73 } 74 if(f && (f->rchild == (*p))){ 75 flag = RCHILD; 76 } 77 78 BSTNode *rc = (BSTNode*)(*p)->rchild; 79 (*p)->rchild = rc->lchild; 80 rc->lchild = *p; 81 *p = rc; 82 83 if(f && (flag==LCHILD)){ 84 f->lchild = *p; 85 } 86 if(f && (flag==RCHILD)){ 87 f->rchild = *p; 88 } 89 } 90 91 //对指针T所指结点为根的二叉树作左平衡选择处理,本算法结束时,指针T指向新的根结点,f为*T的父亲结点。 92 void LeftBalance(BSTree *T, BSTree f){ 93 //lc指向*T的左子树根结点 94 BSTNode *lc = (BSTNode*)(*T)->lchild; 95 BSTNode *rd; 96 //检查*T的左子树的平衡度,并作相应平衡处理 97 switch(lc->bf){ 98 //新结点插在了*T的左孩子的左子树上,要做单右旋处理 99 case LH: 100 lc->bf = (*T)->bf = EH; 101 R_Rotate(T, f); 102 break; 103 //新结点插在了*T的左孩子的右子树上,要做双旋处理 104 case RH: 105 //rd指向*T的左孩子的右子树根 106 rd = lc->rchild; 107 switch(rd->bf){ 108 //修改*T及其左孩子的平衡因子。 109 //提示:建议结合图9.13(b)看,此处*T, lc, rd相当于图中A、B、C结点。 110 case LH: 111 (*T)->bf = RH; 112 lc->bf = EH; 113 break; 114 case EH: 115 (*T)->bf = EH; 116 lc->bf = EH; 117 break; 118 case RH: 119 (*T)->bf = EH; 120 lc->bf = LH; 121 break; 122 } 123 rd->bf = EH; 124 //对*T的左子树lc做左旋平衡处理 125 L_Rotate(&lc, *T); 126 //对*T左右旋平衡处理 127 R_Rotate(T, f); 128 break; 129 default: 130 break; 131 } 132 return ; 133 } 134 135 //和左平衡算法是对称的,此处不再赘述 136 void RightBalance(BSTree *T, BSTree f){ 137 BSTNode *rc = (BSTNode*)(*T)->rchild; 138 BSTNode *ld; 139 switch(rc->bf){ 140 case LH: 141 //提示:建议结合图9.13(d)看,此处*T, rc, ld相当于图中的A、B、C结点。 142 ld = rc->lchild; 143 switch(ld->bf){ 144 case LH: 145 (*T)->bf = EH; 146 rc->bf = RH; 147 break; 148 case EH: 149 (*T)->bf = EH; 150 rc->bf = EH; 151 break; 152 case RH: 153 (*T)->bf = LH; 154 rc->bf = EH; 155 break; 156 } 157 ld->bf = EH; 158 R_Rotate(&rc, *T); 159 L_Rotate(T, f); 160 break; 161 case RH: 162 rc->bf = (*T)->bf = EH; 163 L_Rotate(T, f); 164 break; 165 default: 166 break; 167 } 168 return ; 169 } 170 171 /*平衡二叉树的插入算法 172 * 173 *若在平衡二叉排序树中T不存在和e有相同关键字的结点,则插入一个数据元素为e 174 *的新结点点,并返回TRUE;否则返回FALSE。若因插入而使二叉排序树失去平衡,则 175 *作平衡选择处理,布尔变量taller反映T长高与否。 176 */ 177 int InsertAVL(BSTree *T,BSTree f, ElemType e, Boolean *taller){ 178 if(!(*T)){ 179 //插入新结点,树"长高",置taller为TRUE,并返回TRUE 180 (*T) = (BSTree)malloc(sizeof(BSTNode)); 181 (*T)->data = e; 182 (*T)->bf = EH; 183 (*T)->lchild = (*T)->rchild = NULL; 184 *taller = TRUE; 185 return TRUE; 186 }else{ 187 if(EQ((*T)->data, e)){ 188 //树中已经存在和e相同的结点,不再插入,并返回FALSE 189 *taller = FALSE; 190 return FALSE; 191 } 192 if(LT(e, (*T)->data)){ 193 //应该继续在*T的左子树上进行搜索 194 BSTree *p = malloc(sizeof(BSTree)); 195 *p = (BSTree)((*T)->lchild); 196 if(!InsertAVL(p, *T, e, taller)){ 197 //未插入 198 free(p); 199 return FALSE; 200 } 201 //已插入到*T的左子树中, 更新*T的左子树结点 202 (*T)->lchild = *p; 203 if(*taller){ 204 //左子树"长高",检查*T的平衡度 205 switch((*T)->bf){ 206 case LH: 207 //原本左子树比右子树高,现在左子树上又长高了,需要作左平衡处理 208 LeftBalance(T, f); 209 (*T)->bf = EH; 210 *taller = FALSE; 211 break; 212 case EH: 213 //原本左子树和右子树等高,现在左子树上又长高了,现在*T的左子树比右子树高 214 (*T)->bf = LH; 215 *taller = TRUE; 216 break; 217 case RH: 218 //原本左子树和右子树矮,现在左子树上又长高了,现在*T的左子树比右子树等高 219 (*T)->bf = EH; 220 *taller = FALSE; 221 break; 222 } 223 } 224 free(p); 225 return TRUE; 226 }else{ 227 //应该继续在*T的右子树上进行搜索 228 BSTree *p2 = malloc(sizeof(BSTree)); 229 *p2= (BSTree)((*T)->rchild); 230 if(!InsertAVL(p2, *T, e, taller)){ 231 //未插入 232 free(p2); 233 return FALSE; 234 } 235 //已插入到*T的右子树中, 更新*T的右子树结点 236 (*T)->rchild = *p2; 237 if(*taller){ 238 //右子树"长高",检查*T的平衡度 239 switch((*T)->bf){ 240 case LH: 241 //原本左子树比右子树高,现在右子树上长高了,现在*T的左子树比右子树等高 242 (*T)->bf = EH; 243 *taller = FALSE; 244 break; 245 case EH: 246 //原本左子树和右子树等高,现在右子树上长高了,现在*T的左子树比右子树矮 247 (*T)->bf = RH; 248 *taller = TRUE; 249 break; 250 case RH: 251 //原本左子树和右子树矮,现在右子树上长高了,需要作右平衡处理 252 RightBalance(T, f); 253 (*T)->bf = EH; 254 *taller = FALSE; 255 break; 256 } 257 } 258 free(p2); 259 return TRUE; 260 } 261 } 262 } 263 //二叉树先根遍历算法的函数声明 264 int PreOrderTraverse(BSTree T); 265 //二叉树中根遍历算法的函数声明 266 int InOrderTraverse(BSTree T); 267 //二叉树后根遍历算法的函数声明 268 int PostOrderTraverse(BSTree T); 269 //分别以先、中、后根遍历算法依次打印二叉树中的结点元素的函数声明 270 void print(BSTree T); 271 272 int main(int argc, char *argv[]) 273 { 274 if(argc < 2) 275 return FALSE; 276 int i = 0; 277 ElemType e; 278 Boolean taller; 279 BSTree Tree = NULL; 280 for(i=1; i<argc; i++){ 281 e = atoi(argv[i]); 282 printf("插入数据: %d\n", e); 283 InsertAVL(&Tree, NULL, e, &taller); 284 print(Tree); 285 286 } 287 return TRUE; 288 } 289 290 291 //分别以先、中、后根遍历算法依次打印二叉树中的结点元素的函数实现 292 void print(BSTree T){ 293 printf("先根遍历:\t"); 294 PreOrderTraverse(T); 295 printf("\n"); 296 297 printf("中根遍历:\t"); 298 InOrderTraverse(T); 299 printf("\n"); 300 301 printf("后根遍历:\t"); 302 PostOrderTraverse(T); 303 printf("\n"); 304 } 305 306 307 308 //二叉树先根遍历算法的函数实现 309 int PreOrderTraverse(BSTree T){ 310 if(T){ 311 printf("[%-3d(%-2d)] ", ((BSTNode*)T)->data, ((BSTNode*)T)->bf); 312 PreOrderTraverse((BSTree)T->lchild); 313 PreOrderTraverse((BSTree)T->rchild); 314 } 315 return 0; 316 } 317 318 //二叉树中根遍历算法的函数实现 319 int InOrderTraverse(BSTree T){ 320 if(T){ 321 InOrderTraverse((BSTree)T->lchild); 322 printf("[%-3d(%-2d)] ", ((BSTNode*)T)->data, ((BSTNode*)T)->bf); 323 InOrderTraverse((BSTree)T->rchild); 324 } 325 return 0; 326 } 327 328 //二叉树后根遍历算法的函数实现 329 int PostOrderTraverse(BSTree T){ 330 if(T){ 331 PostOrderTraverse((BSTree)T->lchild); 332 PostOrderTraverse((BSTree)T->rchild); 333 printf("[%-3d(%-2d)] ", ((BSTNode*)T)->data, ((BSTNode*)T)->bf); 334 } 335 return 0; 336 }
运行
标签:特性 har node 代码 alt 超过 时间复杂度 stdio.h 关键字
原文地址:https://www.cnblogs.com/aimmiao/p/9538293.html