当我们当树中添加节点的时候,它其实是通过旋转机制来保证树的平衡的。
旋转机制分为两种:左旋和右旋。
触发机制:当添加一个节点之后,该树不再是一颗平衡二叉树
一、左旋
1)情况1
如下图,添加 12
节点可以发现树失去了平衡。
一旦失去平衡,就会触发旋转机制。
左旋:往左旋转,右旋:往右旋转,旋转的目的就是让这棵树保持平衡。
有的同学会想:右边的节点多,左边的节点少,那肯定是左旋。
答案是对的,但是推导过程是错的。
我们在旋转的时候首先需要确定支点:从添加的节点开始,不断的往父节点找第一个不平衡的节点。将遇到的第一个不平衡的点当做支点进行旋转。再以支点为基准再去判断是左旋还是右旋。
因此我们第一步需要先找支点,从添加的节点 12
网上找,它的父节点是 11
,由于 11
是满足平衡二叉树要求的,因此我们需要往上找 10
节点,此时就发现了,10
节点已经不满足平衡二叉树要求了。
因此现在是以 10
为支点,并且 10
的右边比左边多,此时才断定进行左旋。
步骤:
- 首先找到不平衡的点,以不平衡的点作为支点进行旋转
- 把支点左旋降级,变成左子节点
- 晋升原来的右子节点
这里的不平衡的点就是支点 10
,此时就将支点 10
左旋降级,它原本的右子节点需要晋升,连带着 12
也要一起跑到上面。
2)情况2
如下图添加 12节点
后就破坏平衡了
首先我们需要找到支点:从添加的节点开始,不断的往父节点找第一个不平衡的节点。
一直网上找,可以发现 7节点
是支点。
旋转步骤
- 首先找到不平衡的点,以不平衡的点作为支点进行旋转
- 将根节点的右侧往左拉
- 原先的右子节点变成新的父节点,并把多余的左子节点出让,给已经降级的根节点当右子节点
在旋转的时候,我们先当 9节点
不存在,此时就是按照刚刚的方式进行旋转,旋转完毕后再来看 9节点
。
9
原来是 10
的左子节点,此时 9
节点需要给已经降级的 7
节点的右子节点。
3)经典GIF动画
我们再来看一张风靡各大数据结构网站上的经典图。
根节点的右侧
指的就是红圈圈出来的,此时可以想象是有人要把这一块往上拉。
拉上去后,S
就需要变成新的根节点,原来的根节点 E
就需要降级为根节点的左子节点。
然后再将 S
的左子节点(即蓝色指向的地方),当做是 E
的右子节点。
来看一下动态图,可以发现 S
这个整体被拉上去了。
二、右旋
1)情况1
右旋跟左旋就是反过来的。
如下图,添加 节点1
后破坏平衡了。
既然平衡被破坏,就需要旋转了,第一步依旧是找支点,从添加的节点 1
开始往父节点寻找,找第一个不平衡的节点,将第一个不平衡的节点作为支点来进行旋转。
因此就是能找 4节点
支点,此时就需要将 4节点
当做支点进行右旋。
旋转步骤:
- 以不平衡的点作为支点
- 把支点左旋降级,变成右子节点
- 晋升原来的左子节点
2)情况2
如下图,在刚刚的树上添加了 1节点
,此时就破坏平衡了,因此此时需要通过右旋保证这棵树再次平衡。
此时的支点应该就是 7节点
,此时我们就需要以 7节点
作为支点来进行右旋。
步骤:
- 以不平衡的点作为支点
- 将根节点的左侧往右拉
- 原先的左子节点变成新的父节点,并把多余的右子节点出让,给已经降级的根节点当左子节点
3)经典GIF动画
根节点的左侧就是圈起来的这一块,你可以想象有人要把这块往上拉,拉上去后,这里的 E
就变成了新的根节点。
而原来的根节点 S
就会降级,变成新的根节点的右子节点,那么 E
原来的右子节点就需要给 S
当做是它的左子节点。
在刚刚我们学习的左旋跟右旋,仅仅只是保持平衡的手段。
那么什么时候会触发左旋,什么时候会触发右旋呢?
三、需要旋转的四种情况
这四种情况分别为:左左、左右、右右、右左
1)左左
左左:当根节点左子树的左子树有节点插入,导致二叉树不平衡。
例如在下图的平衡二叉树中添加 节点1 / 节点3
,此时就是在根节点的左子树的左子树上进行添加。
根节点的左子树就是蓝色虚线的地方,即 4节点
。4节点
的左子树就是绿色虚线的地方。
添加后,一共会有两种情况,这两种情况其实是一样的。
添加后,这棵树就已经不平衡了,此时我们就需要从添加的节点开始,不断的往上找不平衡的点作为支点,然后再进行旋转。
此时就会找到 7节点
是支点,因此此时我们需要以7节点
作为支点进行右旋。
在这种情况下,不管是添加节点1还是节点3,都只需要做一次右旋就行了。
因此如果是因为左左导致不平衡,那么一次右旋就能搞定。
2)左右
左右:当根节点左子树的右子树有节点插入,导致二叉树不平衡。
这种情况就比较复杂了,不是一次旋转就能搞定的。
如下图,我需要在根节点左子树的右子树添加节点。
根节点的左子树就是下面蓝色虚线部分,左子树的右子树,就是绿色虚线的这部分。
在此基础上添加一个节点,那我们如何去旋转呢?
有的人说简单,右旋一次不就行了吗?
我们先来找支点,可以发现 7
是支点,因此此时需要 根节点7
作为支点来进行右旋。
旋转完毕后发现,这棵树还没平衡。
因此左右的情况下,一次右旋是不行的。
不着急,我们先将这棵树归位,此时就需要分成两步了
- 将紫色部分进行局部左旋,将左右的情况变成左左
- 此时再进行一次右旋就行了
3)右右
右右跟刚刚的左左其实是反过来的。
右右:当根节点的右子树的右子树有节点插入,导致二叉树不平衡。
例如下图,蓝色虚线的地方为根节点的右子树,即 10节点
。
而 10节点
的右子树为 11节点
,即绿色虚线部分。
此时将 12节点
添加在该处,此时就是:在根节点的右子树的右子树上添加了节点,此时平衡就被破坏了。
因此我们需要进行左旋让这棵树保持平衡。
以根节点 7
作为支点进行旋转,因此在右右的情况下,一次左旋就能搞定了。
4)右左
右左:当根节点右子树的左子树有节点插入,导致二叉树不平衡。
例如下图,根节点的右子树就是蓝色虚线这块,右子树的左子树就是绿色虚线这块。
当添加节点8后,树就不平衡了。
此时就需要进行旋转来平衡二叉树。
但是一次左旋真的能搞定吗?
可以发现,旋转后树还是不平衡的,因此跟刚刚一样,仅仅一次左旋还是不够的。
跟左右一样,也需要进行两步。
将下方局部位置进行右旋,右旋后跟刚刚的右右就一模一样了,然后再进行整体的左旋,此时就搞定了。
四、总结
1)回顾
首先是二叉树,在普通的二叉树中,数据是没有任何规律的,查询效率会比较低,为了解决这个问题,所以有了二叉搜索树。
在添加节点的时候会遵守一个规则:小的存左边,大的存右边,一样的不存。因此它的数据就有规律了,那么在查询起来的时候效率也比较高。
但是它有自己的弊端,它容易出现长短腿的现象,一旦出现长短腿,那么就跟链表差不多了,而且还是单向链表,同样会影响效率。
所以为了解决这个问题,就有了平衡二叉树。在平衡二叉树中,任意节点左右子树高度差不能超过1,因此它的查询效率要更高。
----2)在平衡二叉树中,如何添加节点?
平衡二叉树本身也是一颗二叉查找树,只不过是在二叉查找树的基础上保证了树的平衡而已。
因此它在添加节点的时候也会遵守:小的存左边,大的存右边,一样的不存。
3)在平衡二叉树中,如何查找单个节点?
在树这种数据结构中,查找元素一定是从根节点开始查找的。
4)为什么要旋转?
普通的二叉树跟二叉查找树是不需要旋转的,到目前为止,只有平衡二叉树才需要旋转。
旋转的目的就是:当我们添加一个节点,导致这棵树不平衡了,此时就需要利用旋转让这棵树重新平衡。
5)旋转的触发时机?
当成功添加一个节点后,破坏了这棵树的平衡,就需要进行旋转。
但是如果没有破坏平衡,是不需要进行旋转的。
6)左左是什么意思?如何旋转?
左左:当根节点左子树的左子树有节点插入,导致二叉树不平衡。
一次右旋就ok。
7)左右是什么意思?如何旋转?
左右:当根节点左子树的右子树有节点插入,导致二叉树不平衡。
需要先局部左旋,然后再整体右旋。
8)右右是什么意思?如何旋转?
右右:当根节点的右子树的右子树有节点插入,导致二叉树不平衡。
一次左旋就ok。
9)右左是什么意思?如何旋转?
右左:当根节点右子树的左子树有节点插入,导致二叉树不平衡。
需要先局部右旋,然后再整体左旋。