一、平衡树
- 为什么提出平衡树:普通二叉树在遍历的过程中需要遍历所有节点才能找到对应的数据,这里根据希望根据数据的数值大小,定向的找到数据的存储位置。
- 所以希望平衡树能有以下性质:
- 对于任意节点,其左边的所有点的数值都一定小于其右边所有节点的数值
- 对于节点的两个分支的深度之差不大于1
根据以上需求提出一种平衡树生成算法,该算法只能在以下情况下使用:
- 没有重复数值
- 只能从空节点逐渐插入数值。(无法从普通二叉树直接转为平衡树)
使用一个简单的动图表示算法效果:

二、原理说明
2.1 旋转
如上动图,在对二叉树的操作中,可以拆分为两个动作,左旋和右旋
2.1.1 左旋
口诀:左旋提右,右接左
解释如下图所示,对数字83进行左旋操作,先把右儿子90提上来当爸爸,再把83的右儿子连接到90的左儿子85。

所以总共动了3根线:
- 85——90
- 83——90
- 68——90
所以在进行旋转的时候,需要记录旋转节点83,对应儿子90,以及父节点68。
代码如下(T为旋转点):
1 | if parent.rChild == T: # 记录是父节点的左节点还是右节点 |
2.1.2 右旋
口诀:右旋提左,左接右
解释如下图所示,对数字50进行右旋操作,先把左儿子40提上来,再把50的左儿子连接到40的右儿子45。

所以总共动了3根线:
- 45——40
- 50——40
- 68——40
所以在进行旋转的时候,需要记录旋转节点50,对应儿子40,以及父节点68。
代码如下:
1 | if parent.rChild == T: # 记录是父节点的左节点还是右节点 |
(其实就是和左旋是对称结构,其实可以写成一个函数,但是为了直观,写成两个)
2.2 旋转组合
什么时候需要旋转?
这里需要提出平衡因子这个参数,平衡因子的值只有-1,0,1,平衡因子是根据该节点的右子树的深度减去左子树的深度计算得到。如果不为这3个数值,就会进行旋转操作,达到平衡。
在实际插入数值过程中,会存在两种情况:
- 旋转一次
- 左旋
- 右旋
- 旋转两次
- 左旋,后右旋
- 右旋,后左旋
2.2.1 旋转一次
什么时候旋转一次呢?
找到一个平衡因子不在三个数字之内的节点,假设为2。而插入的数值在起儿子右边,如下图:

在图中:0的平衡因子为2,1的平衡因子为1,2是插入数值,其平衡因子为0。此时,对其进行一次左旋转即可以达到平衡,如下图:

进行右旋的是其相反情况,不再解释。
2.2.2 旋转两次
什么时候旋转两次呢?
找到一个平衡因子不在三个数字之内的节点,假设为2。而插入的数值在其儿子左边,如下图:

在图中:0的平衡因子为2,1的平衡因子为1,2是插入数值,其平衡因子为0。但是此时无法对0进行左旋,所以需要对1先进行右旋,再对0进行左旋。

右旋
左旋
另外一种情况是其相反情况,不再解释。
三、代码实现
1 | # -*- coding: utf-8 -*- |