vlambda博客
学习文章列表

如果构建平衡二叉树?

平衡二叉树顾名思义就是满足某种约束的二叉树。其构建过程自然也应该基于二叉树的基础之上做调整。根据前面的文章,我快速地构建一颗二叉树;然后,根据平衡因子调整二叉树为平衡二叉树。在调整过程中,如果左子树高了,可以右旋调整;如果右子树高了,可以左旋进行调整。下面我就开始探索平衡二叉树的构建过程。

1 平衡因子的计算方法

平衡因子是决定平衡二叉树如何调整的关键性指标,因此,搞清楚平衡因子的计算方法和含义至关重要。根据平衡二叉树的左右子树高度差不大于1的特性,我们可以快速地抽取出平衡因子的计算方法为:

平衡因子 = 左子树深度 - 右子树深度

由于做的减法,结果自然有正负之分。代码片段如下

当结果为正数时,代表左高右低,即左边子树深度大于右子树深度;当结果为负数时,代表左低右高,即右边子树的深度大于左边子树的深度。当平衡因子的绝对值大于等于2时,需要对子树进行旋转调整。

2 节点深度的计算方法

计算平衡因子时,我使用到了节点的深度。那么,结点的深度怎么计算呢?当新建节点时,节点的默认深度为1。如果目标节点为叶子节点,那么深度即为1;如果目标节点不是叶子节点,那么当前节点的深度取左右子节点深度中的最大值加1,可表示为

目标节点深度 = max(左节点深度,右节点深度) + 1

代码片段如下

如果构建平衡二叉树?

上图中,max()方法负责计算出左右子树中最大的深度。calcDepth()方法中获取最大深度,然后在此基础上增加1,即为目标节点的深度。

3 子树旋转流程

为了使二叉树满足平衡二叉树的特性,当平衡因子的绝对值比1大时,则需要对子节点进行调整,使每个节点的平衡因子绝对值小于2。下面,我们就抽取局部子树来探索旋转流程。

3.1 左旋流程

当插入的大节点破坏了根节点的平衡时,根节点应该进行左旋转调整。调整过程如下图

如果构建平衡二叉树?

上图中,当插入60节点时,破坏了40节点的平衡,40节点应该进行左旋调整。调整过程为:将右孩子节点置为父节点,图中即值50节点为原根节点40节点的父节点;如果40节点有父节点,则应该将父节点的后继指针指向50节点;如果50节点有左孩子结点,图中为45结点,则应该将左孩子节点置为原根节点40节点的右孩子。代码片段如下

如果构建平衡二叉树?

当节点调整结束之后,需要重新计算原根节点和原右子节点的深度及平衡因子。

3.2 右旋流程

当插入的小节点破坏了根节点的平衡时,根节点应该进行右旋转调整。调整过程如下图

如果构建平衡二叉树?

上图中,当插入20节点时,破坏了40节点的平衡,40节点应该进行右旋调整。调整过程为:将左孩子节点置为父节点,图中即将30节点置为原根节点40节点的父节点;如果40节点有父节点,则应该将父节点的后继指针指向30节点;如果30节点有右孩子节点,图中为35节点,则应该将右孩子节点置为原根节点40结点的左孩子。代码片段如下

和左旋一样,当节点调整完成之后,会重新计算原根节点和原左子节点的深度及平衡因子。

由于先左旋再右旋先右旋再左旋两种情况可使用左右旋的原子函数进行组合,处于篇幅考虑,我这里就不做详细介绍了。下面我主要探索下构建平衡二叉树的主流程。上文中,我们知道构建平衡二叉树分两步

  • 构建二叉树

  • 按照平衡二叉树约束调整二叉树为平衡二叉树

第一步前面章节已做详细剖析。这里我主要探索二叉树到平衡二叉树的调整过程。调整代码片段如下

如上图,首先会计算当前节点的平衡因子,如果平衡因子大于1,左子树高,即应该进行右旋调整;如果平衡因子小于-1时,由于为负数,表示右子树高,应该进行左旋调整;结点调整结束之后,应该重新计算当前节点的平衡因子和深度。

为了显示地观察平衡二叉树的节点调整,你可以按层输出节点数据进行分析。当然了,如果你愿意,你也可以打个断点看看里面的内容,这更符合程序员的处事逻辑。平衡二叉树为了追求完美的平衡,会在增加或删除节点诱发节点不平衡时,进行多次的旋转调整,这开销是很大的。通常情况下,缺陷就是美是成立的。有时候适当的牺牲掉一些对完美的追求,可能会获得更好的效率、更高的性能,这就好比红黑树。下面的文章,我会继续探索红黑的构建过程及删除路程。