平衡二叉树【旋转的超详细图解】

断了今生、忘了曾经 提交于 2019-12-20 04:17:31

平衡二叉树

 

1、定义:

平衡二叉树,是一种二叉排序树,其中每个节点的左子树和右子树相差的高度不超过1。它是一种高度平衡的二叉排序树。高度平衡:意思是说,要么它是一颗空树,要么它的左子树和右子树都是平衡二叉树。

 

平衡二叉树的出现是为了优化二叉顺序树的查找效率,你可以想象下,二叉顺序树如果顺序添加一个这样的数据{5,4,3,2,1},那么树成了一个链表,查找效率显然不高 。 平衡二叉树首先是一个二叉顺序树,只不过他在每次添加数据的时候会进行自我平衡,来优化查找的效率。

 

2、术语:

最小不平衡子树:指离插入节点最近且以平衡因子的绝对值大于1的节点作为根的子树。 

平衡因子(bf):结点的左子树的深度减去右子树的深度,那么显然-1<=bf<=1;

 

 

3、插入操作

       在平衡二叉树中插入结点与二叉查找树最大的不同在于要随时保证插入后整棵二叉树是平衡的。那么调整不平衡树的基本方法就是: 旋转

 

4、我理解的旋转

旋转是为了解决平衡树的失衡,如何做呢?即将子树高的一边中的一些节点调整到另一边,(并不只是把一边的节点给移到另一边,只是总体看上去,高的一边变矮了。)调整时需要保持树依然 是有序的。

 

 

 

 上面这颗树明显已经不是平衡数,根节点50的平衡因子(bf) 3 -1 > 1 ,此时我们需要调整该树让其再一次到达平衡。我们先把树拆成两部分。如下

  • 第一部分:根节点深度大的子树
  • 第二部分:根节点与深度小的子树

然后我们在将这拆开的两部分组合成一个新的平衡二叉树

为了少打点字,在下面我就把分出来的两部分 一个叫第一部分 , 一个叫第二部分

分开的第一部分,即深度大的子树的根节点(这里是40)将会成为新树的根节点。然后呢?第一部分(针对这个例子)所有的键值是比第二部分的键值小的(二叉顺序数 左子树< 跟节点 < 右子树),所以我们得把第二部分插入到新的根节点的右子树上。 然后我们发现新的根节点的右子树已经有值了,那不得把他给拆下来。将拆下来的这个右子树插到第二部分。我们可以看到第二部分是一定会有一边(可能会有两边)是空的,因为我们把他深度高的子树给他拆下来了 。 而我们把刚拆下来的右子树就可以插到符合顺序的子节点上,这个子节点一定会是空的。这样就完成了旋转。如图:

 

我们来小结一下,我们做了两个步骤(并不完全,后面还会加东西)

  • 将数拆成了两部分
    • 第一部分:根节点深度大的子树
    • 第二部分:根节点与深度小的子树
  • 将第二部分插入到第一部分中: 第二部分插入到第一部分符合二叉顺序树的一边(子节点),如果那里已经有值了,那就把它拆出来插入到第二部分空的一方(子节点)。无值的话插就完事了

 

看到这如果你感觉你理解了辣么来练习一下:

上面说的就是左旋转 和 右旋转,第一个例子是左旋 ,第二个例子是右旋、我在网上找的所有博客都是旋转方法分左右来介绍,让我理解的有点难度。

那什么时候进行左旋 , 什么时候进行右旋呢 ?通过上面的例子我们可以简单的看到哪个(左右)子树深度大就叫哪(左右)旋 。

 

5、需要进行两次的旋转操作

 

以为这样就完了吗 ?不,还有呢,来看下面这个例子!

按这前面的套路一顿操作,然后发现得到依然还是个不平衡的二叉树。。可能有些人看到这就开始了,你在写什么玩意,我看这么久是用你的方法来一顿操作然后得不到正确的结果吗?

呃呃,走远了。我们来分析下为什么对于这个树按照前面的套路得不到平衡树。按套路来、

1、首先拆成两部分,没有什么不对的。额,其实这里没啥分析的

2、我们在合成新树的时候,在将第二部分插入到第一部分满足顺序的子节点时,发现该子节点,并不是一个叶子节点(无子节点的节点),而先前成功的案例都是叶子节点。而且我们看结果问题也确实出现在42这个45的子节点上。

现在问题找到了,那怎么办呢 ??答案是,将第一部分在给他旋转一次,旋转是干什么的,就是为了将左右子树尽量的平衡。虽然第一部分是平衡树,但他这个平衡在我们进行旋转时,我们觉得他这样子不行,那就给他旋转一下。

用旋转后的第一部分在与前面的第二部分进行合成,即可。

这种旋转也有个名字,叫 XX 旋(X的取值是左右。) 比如上面这个例子,他先是进行左旋,然后右旋 ,所以称之为 左右旋 。同理就有右左旋。但是没有左左旋和右右旋,因为这样的一次旋转就可以达到再次平衡.

 

贴一个右左旋转的例子,这里就不在画图演示了,辣么快去练习一下吧。右边是旋转好的结果,

 

6、旋转的总结

 

6.1 步骤

  • 将树拆成两部分
    • 第一部分:根节点深度大的子树
    • 第二部分:根节点与深度小的子树

 

  • 将第二部分插入到第一部分中

第二部分插入到第一部分符合二叉顺序树的一边(子节点),然后

      • a、如果那个节点是空节点,直接加进去就完成了
      • b、如果那个节点不是空节点,且是个叶子节点,那就把它拆出来插入到第二部分空的子节点上(满足顺序的一方)
      • c、如果那个节点不是空节点,且是个非叶子节点,那就需要对第一部分进行一次旋转,然后在进行上面的操做。

 

6.2 分类

  1. 左旋 :深度大的一边是子树,且子树的节点是叶子节点或空节点     步骤:旧根节点的左子节点成为新的根节点,新根节点的右子节点(若存在)成为旧根节点的的左子节点,旧根节点成为新根节点的右子节点
  2. 右旋: 深度大的一边是子树,且子树的节点是叶子节点或空节点     步骤:旧根节点的右子节点成为新的根节点,新根节点的左子节点(若存在)成为旧根节点的的右子节点,旧根节点成为新根节点的左子节点
  3. 左右选旋: 深度大的一边是子树,且左子树的节点是非叶子节点        步骤:我不知道怎么描述。。哈哈
  4. 右左旋:     深度大的一边是子树,且左子树的节点是非叶子节点            步骤:我不知道怎么描述。。哈哈

 

7、插入的什么时候去执行旋转,执行什么旋转,旋转哪些节点?

 

每次进行插入操作的时候,我们都需要进行一次检查,检查插入后是否还是平衡树,如果不是平衡树,那么需要找到最小不平衡子树(可以是整个树),通常就是找到最小不平衡子树的根节点,然后对最小不平衡子树进行旋转操作。

如何判断是不是平衡树:挨个遍历非叶子节点,如过有节点 左子树 - 右子树 的绝对值大于1,那么这棵树就是非平衡的了,找到的这个节点就是最小不平衡子树的根节点。

 

 

JAVA 实现代码

自己写,看到这还写不出来那就是XX 。 我自己也没写 mmp

 

 

 

 

 

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!