AVL树的平衡算法的简化问题

news/2025/3/15 7:51:30/

AVL树是一种紧凑的二叉查找树。它的每个结点,都有左右子树高度相等,或者只相差1这样的特性。文章https://blog.csdn.net/aaasssdddd96/article/details/106291144给出了一个例子。

为了便于讨论,这里对AVL树的结点平衡情况定义2个名称,如果两边左右子树高度相等,称之为真平衡,如果两边高度相差1,称之为准平衡。真平衡和准平衡的这个意义只限于在本文的讨论。准平衡有2种情况,所以实际是3种情况。

AVL树中插入一个新结点时,它的父结点如果是真平衡,那么状态转化为准平衡,树的高度增加了1,因为高度增加,这个父结点需要接着向更高一层的父结点报告这个变化。

如果父结点是准平衡,那么也有2种情况。如果增加的高度是在较矮的一侧,那么父结点状态转为真平衡,操作完成;如果增加的高度是在较高的一侧,那么结点的平衡因子超限,这种情况在上面第一种情况的最后一个变化发生,需要进行平衡调整。调整之后,恢复为真平衡,局部高度不增,操作完成。

这些问题不难。真正让初学者感到烦恼的是平衡调整。调整分4种情况,分别称为“LL-旋转”,“LR-旋转”,“RL-旋转”,“RR-旋转”。后2种和前两种镜像对应。编写这部分代码的时候,最好写完前2种,休息20分钟,再去复制前面的代码,逐条改成它的镜像。休息片刻是为了避免头晕,转来转去转的头一晕,修改镜像时随便漏掉一条,就会带来极为费时难调的bug。这样编程并没有什么不对,这是需要掌握的基本功,而且实际开发过程也会常常碰到需要蛮力硬搞来攻克的难题。

但在这里是否有比硬搞好一点的办法?

如果在构造AVL树时,预分配3个储备结点会怎么样。左、中、右,预先结成一个三角形。但不在AVL树中,而是备用:

struct avl_help {struct node *root;struct triangle {struct node *left;struct node *mid;struct node *right;} help;
};struct avl_help avl;

初始化为:


avl.root=NULL;
avl.help.left = (struct node*)malloc(sizeof(struct node));
avl.help.mid = (struct node*)malloc(sizeof(struct node));
avl.help.right = (struct node*)malloc(sizeof(struct node));avl.help.mid->left=avl.help.left;
avl.help.mid->right=avl.help.right;
avl.help.left->parent=avl.help.mid;
avl.help.right->parent=avl.help.mid;

现在大家都想到了:在AVL树需要发生平衡调整的时候,把已经调好的3个储备结点拿出来,整体替换掉树中需要调整的3个结点,再把替换下来的原来的3个结点做成初始化中的左、中、右三角结构,存回到help中储备,留给下次用。这样调整就完成了。而替换下来的3个结点只要把头结点连到中间结点的左指针,或右指针,具体看那个指针空闲,就重新构成了三角结构。

以“LR型-旋转”为例,调整操作大致是这样子。假设ap0,ap1,ap2三个指针已经分别指向LR型的待调整的上中下3个结点:

help= &avl.help;
help->left->left=ap1->left;
help->left->right=ap2->left;
help->left->tag=(ap2->tag==1?0:-1);help->right->left=ap2->right;
help->right->right=ap0->right;
help->right->tag=(ap2->tag==-1?1:0);help->mid->tag=0;
if(help->left->left)
help->left->left->parent=help->left;
if(help->left->right)
help->left->right->parent=help->left;
if(help->right->left)
help->right->left->parent=help->right;
if(help->right->right)
help->right->right->parent=help->right;
help->mid->parent=ap0->parent;if(help->mid->parent==NULL)
avl->root= help->mid;
else if(help->mid->parent->left==ap0)
help->mid->parent->left=help->mid;
else help->mid->parent->right=help->mid;

而存回替换下来的ap0,ap1,ap2三个结点的代码就是:

ap1->left=ap0;
ap0->parent=ap1;
help->left= ap0;
help->mid=ap1;
help->right=ap2;

其它几种情况可以类推,大同小异,这里就不重复了。


http://www.ppmy.cn/news/1579264.html

相关文章

Java 大视界 -- Java 大数据在智能教育虚拟实验室建设与实验数据分析中的应用(132)

💖亲爱的朋友们,热烈欢迎来到 青云交的博客!能与诸位在此相逢,我倍感荣幸。在这飞速更迭的时代,我们都渴望一方心灵净土,而 我的博客 正是这样温暖的所在。这里为你呈上趣味与实用兼具的知识,也…

P6772 [NOI2020] 美食家

训练角度:图上的状态转移,倍增 → \rightarrow → 优化状态转移; ▍ 题意 精灵王国共有 n n n 座城市,城市从 1 1 1 到 n n n 编号,其中城市 i i i 的美食能为小 W 提供 c i c_i ci​ 的愉悦值。精灵王国的城市…

市场价格波动的影响因素及交易策略优化

市场价格波动的影响因素及交易策略优化 在市场交易过程中,价格波动是不可避免的现象。不同的交易者会基于市场走势、技术分析和资金管理制定不同的交易策略。本文将分析市场价格波动的关键影响因素,并探讨优化交易策略的方法,以帮助交易者更有…

技术与情感交织的一生 (一)

目录 一条朋友圈 静默 至暗时刻 选择 成人高考 歇一下 一条朋友圈 大年初一是我合作伙伴的生日,我称呼他为老高,他发的朋友圈写到:“50岁了,留下的皆是珍贵回忆。” ,看到留言的瞬间,只有一个感觉&a…

Jump Desktop for Mac v9.0.94 优秀的远程桌面连接工具 支持M、Intel芯片

Jump Desktop for Mac 版是macOS平台上的一款远程控制程序,支持Windows和Mac 双平台,通过邮件关联即可帮助设备自动找到桌面并进行操作。 应用介绍 Jump Desktop for Mac 是一款Mac上非常强大和易用的远程桌面控制软件,支持RDP、VNC协议&am…

Excel两列和依次相减

Excel实现左列依次行数的和减去右列依次行数的和: 举例:结余SUM(预付款)-SUM(开支) 公式:SUM($B$2:B2)-SUM($C$2:C2)

计算机安全 第四节:访问控制(中)

中间控制 组:访问权限的管理依靠单个主体是相当麻烦的, 因此通常将用户置于组中,并也可以从用户组取得访问权限 理想状况,所有的访问许可可以通过组成员关系来居中调停通常,安全策略有一些特殊的场合,在这…

C语言学习笔记-进阶(16)编译和连接

1. 翻译环境和运行环境 在ANSI C的任何⼀种实现中,存在两个不同的环境。 第1种是翻译环境,在这个环境中源代码被转换为可执行的机器指令。 第2种是执行环境,它用于实际执行代码。 2. 翻译环境:预编译编译汇编链接 那翻译环境…