AVL树左旋转算法思路与图解
对于数列{4, 3, 6, 5, 7, 8}, 当我们插入8的时候, rightHeight() - leftHeight() > 1成立(也就是当前AVL树中的根节点的BF(平衡因子)> 1了), 此时这个AVL树已经不再是平衡的了, 也就是已经不是一个AVL树了, 所以我们要经过处理之后让其重新平衡
那么如何实现左旋转? —> 此时我们先使用图解的方式来进行演示:
那么我们具体是如何实现对最小生成树根节点的左旋转的?
- 创建一个新的结点newNode(以旧根节点的值来创建, 在上图中为旧根结点的值为4)
- 把新结点的左子树设置为旧根节点的左子树
- 把新结点的右子树设置为旧根结点的右子树的左子树
- 把旧根结点的值修改为新根节点的值(在左旋中就是把旧根结点的值替换为其右子节点的值)
- 左旋转时旧根结点的右子节点的值就是新根节点值
- 因为此时旧根结点的值已经被更新为新根节点的值, 所以我们要将原本的存储新根结点值的结点从树上删去 —> 我们让更新值之后的根节点的右子结点指向其右子结点的右子树, 这样就能删除掉原本存储新根节点值的结点
- 这个时候其实我们就是没有引用指向图中的⑥号结点, 那么这个节点最终就会被Java的垃圾回收机制回收掉
- 更新之后的根节点的左子树指向新的结点newNode
其实上面我们可以发现插入8是插入在了最小不平衡子树的根节点的右子树上, 我们将这种情况导致的不平衡称之为: RR型最小不平衡子树, 我们对于RR型最小不平衡子树的平衡化就是对RR型最小不平衡子树的根节点执行一个左旋转即可
我们的左旋转算法的核心其实只有两步:
- 原本旧根结点成为为现在新根结点的左子树
- 原本新根结点的左子树成为现在旧根节点的右子树
其实上面的六步就是这里核心的实现(我们一定要将这两部核心记住, 至于具体的如何实现我们可以有不同的实现, 但是上面的实现是我们比较推荐的实现方式)
补充:
我们最终左旋的操作肯定是在添加元素的操作中实现的, 由于我们的添加元素的操作是递归实现的, 在添加完元素之后就要开始回溯了, 而在回溯的时候我们就要判断回溯到的结点的BF(平衡因子)是否大于1, 如果大于1, 这个时候就说明以此结点为根的结点是此时不平衡树的最小不平衡子树, 所以我们再判断如果这个最小不平衡子树是一个RR型的, 那么我们就执行对应的左旋转操作即可
- 那么这时候可能有的人会问: 那么我们的旋转操作此时是在递归中执行的, 那么我们的不平衡子树中不仅仅是有最小不平衡子树, 可能还会有其他的不平衡子树,那么当我们对最小不平衡子树执行完旋转的操作之后会不会还会对其他的不平衡子树执行旋转的操作? ,我们按理说只需要对最小不平衡子树执行旋转操作, 执行多个旋转操作不就发生了错误?
- 这个时候注意: 当我们第一次回溯到最小不平衡子树的根节点之后, 我们对这个最小不平衡子树执行了响应的旋转操作之后我们的整个树就重新是一个平衡树了, 所以即使我们的旋转的操作是在递归中进行调用的, 但是最终我们的旋转操作只会在回溯到最小生成树的根节点的时候执行一次而已