简介
什么是二叉平衡树
二叉平衡树,又叫AVL树,它可以是一颗空树,或者具有以下性质的二叉排序树:它的左子树和右子树的高度之差(平衡因子)的绝对值不超过1且它的左子树和右子树都是一颗平衡二叉树
平衡因子
平衡因子 = 左子树高度 - 右子树高度
为什么要有二叉平衡树
二叉搜索树可以在一定程度上提高搜索效率,但当原序列有序,如{ 1, 2, 3, 4, 5, 6},二叉树的结构如图,此时构造的二叉搜索树为右斜树,同时二叉树退化成单链表
此时查找元素6
所需的的次数为6
二叉搜索树的查询效率取决于树的高度,因此保持树的高度最小,即可保证树的查找效率。同样的序列 A,将其改为下图的方式存储,查找元素 6 时只需比较 3 次,查找效率提升一倍
由上面可得:当节点数目一定时,树的左右两端保持平衡,查询效率最高,由此引出二叉平衡树
失衡与调整
失衡
如图为一颗二叉平衡树
插入一个 92 后
此时根节点 59 的左子树高度为 1 右子树高度为 3,平衡因子为 -2,树失去平衡,且以 59 为节点的树就称为最小失衡子树
最小失衡子树
在新插入的结点向上查找,以第一个平衡因子的绝对值超过 1 的结点为根的子树称为最小不平衡子树。也就是说,一棵失衡的树,是有可能有多棵子树同时失衡的。而这个时候,我们只要调整最小的不平衡子树,就能够将不平衡的树调整为平衡的树
调整
平衡二叉树的失衡调整主要是通过旋转最小失衡子树来实现的
根据旋转的方向有两种处理方式,左旋与右旋
左旋
当树的右子树高于左子树时,对该树进行左旋操作:
- 节点的右孩子替代此节点位置
- 右孩子的左子树变为该节点的右子树
- 节点本身变为右孩子的左子树
右旋
右旋操作与左旋类似,操作流程为:
- 节点的左孩子代表此节点
- 节点的左孩子的右子树变为节点的左子树
- 将此节点作为左孩子节点的右子树
二叉平衡树的四种插入节点情况
假设一颗 AVL 树的某个节点为 A,有四种操作会使 A 的左右子树高度差大于 1,从而破坏了原有 AVL 树的平衡性。平衡二叉树插入节点的情况分为以下四种:
插入方式 | 描述 | 旋转方式 |
---|---|---|
LL | 在 A 的左子树根节点的左子树上插入节点而破坏平衡 | 右旋 |
RR | 在 A 的右子树根节点的右子树上插入节点而破坏平衡 | 左旋 |
LR | 在A的左子树根节点的右子树上插入节点而破坏平衡 | 先左旋后右旋 |
RL | 在 A 的右子树根节点的左子树上插入节点而破坏平衡 | 先右旋后左旋 |
LL
代码实现右旋
1 | //返回值:新父节点 |
RR
代码实现左旋
1 | //返回值:新父节点 |
LR
LR 需要进行两部操作:
- 对失衡节点的左孩子进行左旋操作,即上述 RR 情形操作
- 对失衡节点做右旋操作,即上述 LL 情形操作
代码实现
1 | //返回值:新父节点 |
RL
RL 需要进行两部操作:
- 对失衡节点的右孩子进行右旋操作,即上述 LL 情形操作
- 对失衡节点做左旋操作,即上述 RR 情形操作
代码实现
1 | //返回值:新父节点 |
小结
- 在所有的不平衡情况中,都是按照先 寻找最小不平衡树,然后 寻找所属的不平衡类别,再 根据 4 种类别进行固定化程序的操作
- LL , LR ,RR ,RL 其实已经为我们提供了最后哪个结点作为新的根指明了方向,只要记住这四种情况,可以很快地推导出所有的情况