平衡二叉树(一)

为什么要有平衡二叉树

我一直认为,要想学好数据结构和算法,一定先要深刻了解它的思想,在加以代码辅助。

为什么要有平衡二叉树
冒泡、选择、快速、插入等排序算法基于数组的,查找方便,插入删除麻烦。用链表的话,插入删除方便、但是查找麻烦了,每次都需要从头向尾查找。
这时,平衡二叉树就应运而生了,兼顾了链表的方便插入、删除的优点时,有减少了查找的次数。
二叉树说到底,大概勉强可以归于链表一类的,所以方便插入删除,是可以理解的。那么减少了查找的次数又怎么理解呢?
不知道大家有没有玩过猜数字的游戏,游戏规则:

给出一串数字纸牌(100以内的正整数),甲随意抽出一张纸牌,由乙保存。纸牌的内容甲不知道,乙知道。甲每次猜纸牌的内容时,乙只能提示甲猜的数字有没有比正确的数字大。问甲采取什么样的规则才能最快猜出纸牌的内容。

答案很简单,每次都猜中间数,缩小数字范围,然后再次猜缩小后的范围的中间数。这样才能最快猜出来。
同样,平衡二叉树也是类似的规则,每次都是从差不多中间的范围开始猜。这样会大大减少查找比较的次数。

什么是平衡二叉树

平衡二叉树(Balanced BinaryTree)具有以下性质:它是一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。

这是百度上对平衡二叉树的定义,简单的来说,就是在二叉树的基础之上又添加了两个限制:

  1. 对于任何一个节点而言、该节点的左子节点的值比自己小,右子节点的值比自己大。(当然、左右节点的值、可以为空)
  2. 对于任何一个节点而言,该节点的左子树的深度与右子树的深度的差不超过1。

平衡二叉树相比较二叉树多出了两个限制,一定不是白白多出的,一定是为方便查找、插入提供服务的。第一个限制保证了范围,假设以某个节点为中间数,那么该节点左侧都是小于该节点的,右侧都是大于该节点的,我们要查找的数字,如果比该节点小,走左侧,否则走右侧。第二个限制,则保证两边的范围所拥有的节点个数是差不多的。不能左侧只有1个节点,右侧有100个节点,这样的话,又差不多是从头查找到尾。如下图,以51为中间数,左侧都比51小,右侧比51大。并且左侧有50个数,右侧有49个数,个数也是差不多了。
在这里插入图片描述

怎么形成平衡二叉树

最关键的就是如何构建一个平衡二叉排序树。我会先将大体做法说明一下,然后辅以图片再进一步进行说明。
平衡二叉树就像是一个天枰一样,哪边重,向哪边歪。我们要做的就是不断的调整两边的重量,以保证两边处于一个平衡之中。只不过平衡二叉树这个天秤比较特别。两边的重量是由两边的子树的深度决定的,哪个字数的深度大,谁就重。并且只要两边的重量不超过1,天秤就可以保持一个平衡。

网上,以及其他的资料中大多说只有在子树的深度差超过1的情况下,需要调整子树,这样说也没有错。但是在我看来,二叉树其实在两种情况下需要调整:

  1. 两颗子树的深度差大于1,需要向深度小的的子树方向进行旋转。
  2. 两颗子树的深度差大于0,需要向深度小的的子树方向进行旋转。

注意:第二种情况主要是为第一种情况服务,即只有出现了第一种情况,才会有机会根据第二种情况进行调整。
说到这,大家一定很迷惑,都是些什么玩意啊,怎么情况更复杂了啊。哈哈,没关系,接下来,我会为大家进行详细的讲解。记住,现在的复杂是为了以后的简单。否则,我也没有必要写这篇博客,因为我发现网上的讲解在面对简单的情况下很容易理解,但是遇到复杂的情况下,大家就不知道怎么旋转了。

对于一个节点,只要这个节点的子树的深度比另一个子树的的深度大2及以上,那么就需要将以这个节点为根节点的二叉树向深度较小的那一边进行倾斜。我们做任何东西都是由简到繁,为了方便理解,我们先以只有3个节点的二叉树进行平衡旋转。
在只有3个节点的情况下,会形成多少种二叉树呢?只有5种,如下图所示
在这里插入图片描述
从上图中我们可以很清楚的看到,除却第一幅图符合平衡二叉树,其余四幅图都不符合,需要进行调整。后面四幅图涵盖了平衡二叉树中所有的不同的情况,接下来将会对它们进行各自的分析。
对于后面四种情况,网上针对每一种情况给出了相应的解决方法,什么LL型、LR型、RR型、RL型。我的头都晕了,原谅我的脑容量真的很小,记不住太多的东西。
那有什么简单好记的方法吗?有。如果你真的仔细思考过,你就会发现网上针对后面四种情况的解法其实是一种。在此且听我细细道来。首先我先给出结论,然后在结合图片给予相应的解释:

对于一个平衡二叉树,如果在插入一个新的节点的情况下,这个树变得不平衡了。那么从插入的新节点开始,沿着新节点的父祖节点方向,向上找到第一颗不平衡的子树(许多情况下,一颗树不平衡是因为它的子树本身就是不平衡的,我们找的就是那颗最底层,辈分最低的那颗不平衡的子树),为了方便说明,假设这颗不平衡的子树 的左子树的深度比右子树的深度大。那么这棵树需要向右子树方向(即右方)旋转。
那么要想成功符合平衡二叉树的规则进行旋转,那么有一点必须是遵守的,就是左子树的左子树的深度一定要大于左子树的右子树的深度,不知道大家有没有理解,看图说话,就是红色树的深度一定要大于绿色树的深度。
在这里插入图片描述

原因如下图:
在这里插入图片描述
由图我们可以知道,要想成功右旋,必须保证红树的深度比绿树大。如果绿树的深度更大,那么把绿树接到伪根节点后,就造成了新的二叉树的右子树的深度比左子树的深度大2,还是不平衡二叉树,这么一番操作没有任何意义。所以必须要保证左子树的左子树(红色树)的深度比左子树的右子树(绿色树)的深度大。如果真的出现了绿色树的深度比红色树的深度大,那么需要对左子树这个二叉树进行左旋调整。
结论:左子树比右子树的深度大2时,需要右旋。因为右旋,所以保证左子树的左子树的深度比左子树的右子树的深度大(或者等于),否则对左子树的左子树进行左旋(这就是我在上文所说的:对于左子树而言,它的两颗子树的深度差大于0,需要向深度小的的子树方向进行旋转)。
右子树比左子树的深度大2时,需要左旋。因为左旋,所以保证右子树的右子树的深度比右子树的左子树的深度大(或者等于),否则对右子树的右子树进行右旋

可能到这儿,大家还不是很懂,没关系,我会结合具体的事例,进行讲解。

LL型

在这里插入图片描述
1、从1节点开始向上寻找第一个不平衡二叉树,以2为节点的子树是平衡二叉树,继续向上找,以3为节点的子树不是平衡二叉树。接下来对这个二叉树进行平衡调整。
2、左子树深度为2,右子树深度为0,需要右旋
3、因为需要右旋,要保证3树的左子树的左子树(即以1为根节点的子树)的深度大于等于3树的左子树的右子树(此图中为空),由图可知,2节点的左子树深度为1,右子树深度为0,满足条件。
4、进行右旋。
在这里插入图片描述

LR型

在这里插入图片描述
1、从2节点开始向上寻找第一个不平衡二叉树,以1为节点的子树是平衡二叉树,继续向上找,以3为节点的子树不是平衡二叉树。接下来对这个二叉树进行平衡调整。
2、左子树深度为2,右子树深度为0,需要对3树(以3节点为根节点的二叉树)右旋
3、因为需要右旋,要保证3树的左子树的左子树(是个空树)的深度大于等于3树的左子树的右子树,由图可知,1节点的左子树深度为0,右子树深度为1,不满足条件。所以先对左子树(简称为1子树、即以1节点为根节点的子树)左旋。
3.1、因为对1子树左旋,所以要保证1子树的右子树的右子树(此图中为空)的深度大于等于1子树的右子树的左子树(此图中为空)。由图知,都是空树,符合条件,可以左旋。
在这里插入图片描述
这所做的一切就是保证,做孩子节点成为新的根节点后,这个新的根节点是一个平衡二叉树。
4、此时,满足了3树的左子树的左子树的深度比3树的左子树的右子树深度大,可以进行右旋了。
在这里插入图片描述

RR型

在这里插入图片描述
1、从3节点开始向上寻找第一个不平衡二叉树,以2为根节点的子树是平衡二叉树,继续向上找,以1为根节点的子树不是平衡二叉树。接下来对这个二叉树进行平衡调整。
2、右子树深度为2,左子树深度为0,需要左旋
3、因为需要左旋,要保证1树的右子树的右子树(即以3为根节点的子树)的深度大于1树的右子树的左子树,由图可知,2节点的左子树深度为0,右子树深度为1,满足条件。
4、进行左旋
在这里插入图片描述

RL型

在这里插入图片描述
1、从2节点开始向上寻找第一个不平衡二叉树,以3为根节点的子树是平衡二叉树,继续向上找,以1为根节点的子树不是平衡二叉树。接下来对这个二叉树进行平衡调整。
2、右子树深度为2,左子树深度为0,需要左旋
3、因为需要左旋,要保证其右子树的右子树(此图中为空)的深度大于右子树的左子树,由图可知,3节点的左子树深度为1,右子树深度为0,不满足条件。所以先对右子树(简称为3子树、即以3节点为根节点的子树)右旋。
3.1、因为对3子树右旋,所以要保证3子树的左子树的左子树(此图中为空)的深度大于等于3子树的左子树的右子树(此图中为空)。由图知,都是空树,符合条件,可以右旋。
在这里插入图片描述
4、此时,满足了1树的右子树的右子树的深度比1树的右子树的左子树深度大,可以进行左旋了。
在这里插入图片描述
以上四种情况涵盖了平衡二叉树的所有情况,不知道大家有没有看懂。也会大家会说,对于这些简单的情况,不需要你这么花里胡哨,我也能很快调整。哈哈,的确如此,但是面对一个不平衡的二叉树,无论是一个简单的情况,还是复杂的情况,我们都需要一个统一的“公式”,调整的时候,直接套用公式即可。

例子

好了,简单的额情况我们已经分析完了,接下来,我会用多个数据,来演示一下如何形成平衡二叉树。
依次插入以下数据,并保证每个数据插入后,都要调整成一个平衡二叉树:
{3,2,1,4,5,6,7,10,9,8}
1、插入3
在这里插入图片描述
2、插入2
在这里插入图片描述
3、插入1
在这里插入图片描述
此时,从下向上找到3树是不平衡二叉树,需要右旋。右旋需要保证2子树的左子树比2子树的右子树深度大,满足条件。
在这里插入图片描述
4、插入4
在这里插入图片描述
5、插入5
在这里插入图片描述
从插入的节点沿着父祖节点的方向找到,第一个不平衡的二叉树,就是3子树。3子树需要左旋。因为左旋,保证4子树的右子树比4子树的左子树深度大,满足条件,进行左旋。
在这里插入图片描述
6、插入6
在这里插入图片描述
从插入的节点沿着父祖节点的方向找到,第一个不平衡的二叉树,就是2树。2子树需要左旋。因为左旋,保证4子树的右子树比4子树的左子树深度大,满足条件,进行左旋。
在这里插入图片描述
7、插入7
在这里插入图片描述
从插入的节点沿着父祖节点的方向找到,第一个不平衡的二叉树,就是5树。5子树需要左旋。因为左旋,保证6子树的右子树比6子树的左子树深度大,满足条件,进行左旋。
在这里插入图片描述
8、插入10
在这里插入图片描述
9、插入9
在这里插入图片描述
从插入的节点沿着父祖节点的方向找到,第一个不平衡的二叉树,就是7树。7子树需要左旋。因为左旋,保证10子树的右子树比10子树的左子树深度大,不满足条件。
所以先对10子树右旋。
在这里插入图片描述
此时保证了10子树的右子树比左子树大,然后在对7子树左旋。
在这里插入图片描述
10、插入8
在这里插入图片描述
从插入的节点沿着父祖节点的方向找到,第一个不平衡的二叉树,就是6树。6子树需要左旋。因为左旋,保证9子树的右子树比9子树的左子树深度大,但是我们可以很清楚的从图中看出来,9子树的右子树深度为1,左子树的深度为2,不满足条件。
所以在对9子树右旋。因为对9子树右旋,所以保证7子树的左子树的的深度比7子树的右子树的深度大或者等于。很明显,不满足条件。
所以先对7子树左旋
在这里插入图片描述
此时9子树满足了其左子树的左子树比其左子树的右子树大,所以可以对9子树右旋了
在这里插入图片描述
此时满足了6子树满足了其右子树的右子树比其右子树的左子树大,所以可以进行左旋了
在这里插入图片描述

那么最终的平衡二叉树:
在这里插入图片描述
这个平衡二叉树的中序遍历的顺序:1,2,3,4,5,6,7,8,9,10,是按照从小到大顺序排列的。

总结

这篇博客就到这了,不知道大家懂了没有,这篇文章的关键,就是你得理解为什么右旋时,要求左子树的左子树一定要比左子树的右子树的深度大或者等于,大家可以用反证法试一试就明白了。

猜你喜欢

转载自blog.csdn.net/qq_43579103/article/details/99689704