Author: Mike
问题背景
时间复杂度
- 平衡二叉树基于二叉查找树的特性,在查找的过程中,在每一层只会访问一个结点,也即平衡二叉树的时间复杂度与树的高度有关。
对于一颗 满二叉树,时间复杂度为
故 O(n) = logN;
- 树的层数越少,查找数据的平均查找时间就会越少
平衡二叉树是具有以下性质的二叉查找树:对于树中的任意一个结点,都有该结点的左子树的高度与右子树的高度之差的绝对值小于2。与普通的BST相比,AVL树只是多定义了旋转操作,使得当左右子树的高度差的绝对值大于或者等于2时,平衡树可以自动地进行树形调整,以重新满足上述性质。
存在的问题
当所给予的关键字序列是有顺序的时候,将会出现如下尴尬的二叉树。树的结构会变为单向左子树或者单向右子树,二叉树退化为单向链表,时间复杂度为 O(n),
结论
尽可能降低树的高度,主要是通过二叉树的旋转来达到降低树高度的目的,也就是接下来讨论的内容。
旋转
先来看几个概念
平衡因子
某结点的 左子树 与 右子树 的高度(深度) 差 即为该结点的平衡因子(BF,Balance Factor)。平衡二叉树上所有结点的平衡因子只可能是 -1,0 或 1。
举例子如下, 结点旁边数据即为平衡因子
最小不平衡子树
最小不平衡子树以离插入结点最近、且平衡因子绝对值大于 1 的结点作根结点的子树。
结点失衡
调整失衡结点的手段为旋转操作,一共有日下四种旋转
LL 在结点A的左孩子(L)的左子树(L)插入新结点导致不平衡 –> LL单右旋操作
RR 在结点A的右孩子(R)的右子树(R)插入新结点导致不平衡 –> RR单左旋操作
LR 在结点A的左孩子(L)的右子树(R)插入新结点导致不平衡 –> LR旋转。
可以先假设在D结点有一个右孩子,为null,也即虚构的右孩子,由此可见 LR 旋转 = LL旋转 + RR旋转
具体例子
- RL 在结点A的右孩子(R)的左子树(L)插入新结点导致不平衡 –> RL旋转,与LR旋转的理论类似,RL 旋转 = RR旋转 + LL旋转
具体例子
操作
平衡二叉树的增删查改操作
https://zhuanlan.zhihu.com/p/25320155?refer=hinus
结点定义
1 | public class AVLNode<T extends Comparable> { |
LL
1 | /** |
RR
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19/**
* 右右单旋转(RR旋转) x变为w的根结点, w变为x的左子树
*
* @return
*/
private AVLNode<T> singleRotateRight(AVLNode<T> w) {
AVLNode<T> x = w.right;
w.right = x.left;
x.left = w;
//重新计算x/w的高度
x.height = Math.max(height(x.left), w.height) + 1;
w.height = Math.max(height(w.left), height(w.right)) + 1;
//返回新的根结点
return x;
}
LR
1 | /** |
RL
1 | /** |
插入
与BST(二叉查找树)的插入实现原理一样,使用递归算法,根据值大小查找到插入位置,然后进行插入操作,插入完成后,我们需要进行平衡判断,评估子树是否需要进行平衡修复,需要则利用上述的四种情景套入代码即可,最后要记得重新计算插入结点路径上的高度
1 | /** |