平衡二叉树的构造c语言,平衡二叉树(C语言,又称AVL树,实现LeftBalance,RightBalance)...

本文详细介绍了AVL树的概念及其保持平衡的重要性。通过左旋、右旋和双旋操作来确保二叉排序树在插入新节点后仍保持平衡。插入过程中,根据节点的平衡因子和插入结果进行相应的旋转调整,以维持平衡因子在-1、0、1之间。文章还提供了C语言实现的插入函数和平衡调整函数,展示了如何在实际操作中应用这些概念。
摘要由CSDN通过智能技术生成

1. 背景

因为二叉排序树在平衡的情况下查询效率最佳,AVL树让二叉排序树节点变动后维持平衡(左子树高度-右子树高度 差的绝对值<2)

2. 算法原理

根据节点的平衡因子及插入结果,实际修正二叉树,把不平衡消灭最初时刻

2.1 关于旋转

左旋:子树以右孩子为轴心,整体左旋转,旋转后的状态:原根的右孩子成为新根,原根成为新根的左孩子,新根的原左孩子成为其新左孩子的右孩子(中序遍历顺序未变)

右旋:子树以左孩子为轴心,整体右旋转,旋转后的状态:原根的左孩子成为新根,原根成为新根的右孩子,新根的原右孩子成为其新右孩子的左孩子(中序遍历顺序未变)

2.2 出现不平衡时,调整的原则是

根节点和它孩子必须符号相同(即都是左高或右高,根节点和它的左或右孩子的bf要么都是正数,要么都是负数)。符号一致直接旋转,不一致则需两次操作(即双旋)

(注:出现双旋转时,左孩子或右孩子所在子树需要先旋转,并不会遵守符号相同原则,这一步可以称作符号适配)

2.3 造成不平衡的情况分类及调整方案(4类)

LL(左子树高,左孩子的左子树高)

调整方案:直接右旋转 + bf修正

note-133199.html

LR(左子树高,左孩子的右子树高)

调整方案:右孩子子树左旋 => 右旋转 + bf修正

note-133199.html

RR(右子树高,右孩子的右子树高)

调整方案:直接左旋转 + bf修正

note-133199.html

RL(右子树高,右孩子的左子树高)

调整方案:左孩子子树右旋转 => 左旋转 + bf修正

note-133199.html

2.2 其它

==下面只整理了插入操作相关原理==

二叉树中的节点有一个平衡因子(bf,值为常量-1,0,1)分别标识本节点为根的子树当前是右高,等高,左高

插入操作附带更新tag( taller)来判断某节点是否长高

插入函数的递归调用回溯时,会根据所在节点的bf值决定是否调整二叉树(旋转)及更新bf值

(注:一些细节见代码注释)

3. 代码实现

(纸张不够用,最后几张图有的多步合作一步了)

note-133199.html #include #include #include // 平衡因子(bf)的值 #define LH 1 #define EH 0 #define RH -1 typedef struct BiTNode { int data; int bf; struct BiTNode *lchild, *rchild; }BiTNode, *BiTree; void L_Rotate(BiTree *); void R_Rotate(BiTree *); bool InsertAVL(BiTree *, int, bool *); void LeftBalance(BiTree *); void RightBalance(BiTree *); void traverse_mid(BiTree); // 左旋转 void L_Rotate(BiTree *P) { BiTree R; R = (*P)->rchild; (*P)->rchild = R->lchild; R->lchild = *P; *P = R; } // 右旋转 void R_Rotate(BiTree *P) { BiTree L; L = (*P)->lchild; (*P)->lchild = L->rchild; L->rchild = *P; *P = L; } // 左调整 void LeftBalance(BiTree *P) { // 执行到此函数说明本子树的bf已经变为2(并没有显示赋值2,且左孩子树高 // 左孩子不可能为EH,只可能是LH或RH,因为如果是EH,则上一次EH时就已经是不平衡的了 BiTree L, Lr; L = (*P)->lchild; switch (L->bf) { // 匹配手绘图中的LL,右旋处理 case LH: (*P)->bf = L->bf = EH; R_Rotate(P); break; // 匹配手绘图中的LR,双旋处理 case RH: Lr = L->rchild; // 根据Lr的bf修改平衡因子(下面这些bf修改建议亲自手绘下面3种情况来理解, 不需要硬记) // 先修改bf,不影响旋转 switch (Lr->bf) { // 匹配LRB case LH: (*P)->bf = RH; L->bf = EH; break; // 匹配LRA case EH: (*P)->bf = L->bf = EH; break; // 匹配LRC case RH: (*P)->bf = EH; L->bf = LH; break; } Lr->bf = EH; L_Rotate(&(*P)->lchild); R_Rotate(P); } } // 右调整 void RightBalance(BiTree *P) { BiTree R, Rl; R = (*P)->rchild; switch (R->bf) { case RH: (*P)->bf = R->bf = EH; L_Rotate(P); break; case LH: Rl = R->lchild; switch (Rl->bf) { // 匹配RLB case LH: (*P)->bf = EH; R->bf = RH; break; // 匹配RLA case EH: (*P)->bf = R->bf = EH; break; // 匹配RLC case RH: (*P)->bf = LH; R->bf = EH; break; } Rl->bf = EH; R_Rotate(&(*P)->rchild); L_Rotate(P); } } // 插入函数 bool InsertAVL(BiTree *P, int e, bool *taller) { if (!*P) { printf("insert %d\n", e); *P = (BiTree)malloc(sizeof(BiTNode)); (*P)->bf = EH; (*P)->data = e; (*P)->lchild = (*P)->rchild = NULL; *taller = true; } else { if (e == (*P)->data) { *taller = false; return false; } if (e < (*P)->data) { // 插入失败肯定是上面这种找到了匹配的节点 if (! InsertAVL(&(*P)->lchild, e, taller)) { return false; } if (*taller) { // 大画数据结构中的这个表达式有一处刊误 switch ((*P)->bf) { // 左子树插入成功 case LH: printf("At %d bf now %d after insert %d LeftBalance\n", (*P)->data, (*P)->bf, e); LeftBalance(P); // 原来左高,现左长高,则不平衡,调整 *taller = false; // 未长高(不平衡已经消除) break; case EH: (*P)->bf = LH; // 原来平衡,现左长高,则左高 *taller = true; // 长高 break; case RH: (*P)->bf = EH; // 原来右高,现左长高,则平衡 *taller = false; // 未长高 break; } } } else { if (! InsertAVL(&(*P)->rchild, e, taller)) { return false; } if (*taller) { // 右子树插入成功 switch ((*P)->bf) { case LH: (*P)->bf = EH; // 原来左高,现右长高,则平衡 *taller = false; // 未长高 break; case EH: (*P)->bf = RH; // 原来平衡,现右长高,则右高 *taller = true; // 长高 break; case RH: printf("At %d bf now %d after insert %d RightBalance\n", (*P)->data, (*P)->bf, e); RightBalance(P); // 原来右高,现右长高,则调整 *taller = false; // 未长高(不平衡已经消除) break; } } } } return true; } void traverse_mid(BiTree T) { if (!T) return; traverse_mid(T->lchild); printf("%d[%d] ", T->data, T->bf); traverse_mid(T->rchild); } int main(void) { // 标识树是否长高 bool taller; int i; int a[10] = {3, 2, 1, 4, 5, 6, 7, 10, 9, 8}; BiTree T = NULL; for (i=0; i<10; i++) { InsertAVL(&T, a[i], &taller); traverse_mid(T); putchar('\n'); } printf("finally正序遍历:\n"); traverse_mid(T); putchar('\n'); return 0; }

output [root@8be225462e66 c]# gcc avl.c && ./a.out insert 3 3[0] insert 2 2[0] 3[1] insert 1 At 3 bf now 1 after insert 1 LeftBalance 1[0] 2[0] 3[0] insert 4 1[0] 2[-1] 3[-1] 4[0] insert 5 At 3 bf now -1 after insert 5 RightBalance 1[0] 2[-1] 3[0] 4[0] 5[0] insert 6 At 2 bf now -1 after insert 6 RightBalance 1[0] 2[0] 3[0] 4[0] 5[-1] 6[0] insert 7 At 5 bf now -1 after insert 7 RightBalance 1[0] 2[0] 3[0] 4[0] 5[0] 6[0] 7[0] insert 10 1[0] 2[0] 3[0] 4[-1] 5[0] 6[-1] 7[-1] 10[0] insert 9 At 7 bf now -1 after insert 9 RightBalance 1[0] 2[0] 3[0] 4[-1] 5[0] 6[-1] 7[0] 9[0] 10[0] insert 8 At 6 bf now -1 after insert 8 RightBalance 1[0] 2[0] 3[0] 4[-1] 5[0] 6[1] 7[0] 8[0] 9[0] 10[0] finally正序遍历: 1[0] 2[0] 3[0] 4[-1] 5[0] 6[1] 7[0] 8[0] 9[0] 10[0] [root@8be225462e66 c]#

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值