平衡二叉树构建的基本思想就是在构建二叉排序树的过程中,每当插入一个结点时,先检查是否因插入而破坏了树的平衡性,若是,则找出最小不平衡子树。在保持二叉排序树特性的前提下,调整最小不平衡子树中各结点之间的链接关系,进行相应的旋转,使之成为新的平衡子树。

    为了能在讲解算法时轻松一些,我们先讲一个平衡二叉树构建过程的例子。假设我们现在有一个数组a[10]={3,2,1,4,5,6,7,10,9,8}需要构建二叉排序树。在没有学习平衡二叉树之前,根据二叉排序树的特性,我们通常会将它构建成如图8-7-4的图1所示的样子。虽然它完全符合二叉排序树的定义,但是对这样高度达到8的二叉树来说,查找是非常不利的。我们更期望能构建成如图8-7-4的图2的样子,高度为4的二叉排序树才可以提供高效的查找效率。那么现在我们就来研究如何将一个数组构建出图2的树结构。
    image.png
    对于数组a[10]={3,2,1,4,5,6,7,10,9,8}的前两位3和2,我们很正常地构建,到了第3个数“1”时,发现此时根结点“3”的平衡因子变成了2,此时整棵树都成了最小不平衡子树,因此需要调整,如图8-7-5的图1(结点左上角数字为平衡因子BF值)。因为BF值为正,因此我们将整个树进行右旋(顺时针旋转),此时结点2成了根结点,3成了2的右孩子,这样三个结点的BF值均为0,非常的平衡,如图8-7-5的图2所示。
    image.png
    然后我们再增加结点4,平衡因子没有超出限定范围(-1,0,1),如图3。增加结点5时,结点3的BF值为-2,说明要旋转了。由于BF是负值,所以我们对这棵最小平衡子树进行左旋(逆时针旋转),如图4,此时我们整个树又达到了平衡。

    继续,增加结点6时,发现根结点2的BF值变成了-2,如图8-7-6的图6。所以我们对根结点进行了左旋,注意此时本来结点3是4的左孩子,由于旋转后需要满足二叉排序树特性,因此它成了结点2的右孩子,如图7。增加结点7,同样的左旋转,使得整棵树达到平衡,如图8和图9所示。
    image.png
    当增加结点10时,结构无变化,如图8-7-7的图10。再增加结点9,此时结点7的BF变成了-2,理论上我们只需要旋转最小不平衡子树7、9、10即可,但是如果左旋转后,结点9就成了10的右孩子,这是不符合二叉排序树的特性的,此时不能简单的左旋,如图11所示。
    image.png
    仔细观察图11,发现根本原因在于结点7的BF是-2,而结点10的BF是1,也就是说,它们俩一正一负,符号并不统一,而前面的几次旋转,无论左还是右旋,最小不平衡子树的根结点与它的子结点符号都是相同的。这就是不能直接旋转的关键。那怎么办呢?

    不统一,不统一就把它们先转到符号统一再说,于是我们先对结点9和结点10进行右旋,使得结点10成了9的右子树,结点9的BF为-1,此时就与结点7的BF值符号统一了,如图8-7-7的图12所示。

    这样我们再以结点7为最小不平衡子树进行左旋,得到图8-7-8的图13。接着插入8,情况与刚才类似,结点6的BF是-2,而它的右孩子9的BF是1,如图14,因此首先以9为根结点,进行右旋,得到图15,此时结点6和结点7的符号都是负,再以6为根结点左旋,最终得到最后的平衡二叉树,如图8-7-8的图16所示。
    image.png
    相信大家应该明白,所谓的平衡二叉树,其实就是在二叉排序树创建过程中保证它的平衡性,一旦发现有不平衡的情况,马上处理,这样就不会造成不可收拾的情况出现。通过刚才这个例子,你会发现,当最小不平衡子树根结点的平衡因子BF是大于1时,就右旋,小于-1时就左旋,如上例中结点1、5、6、7的插入等。插入结点后,最小不平衡子树的BF与它的子树的BF符号相反时,就需要对结点先进行一次旋转以使得符号相同后,再反向旋转一次才能够完成平衡操作,如上例中结点9、8的插入时。