序
纸上得来终觉浅,觉知此事要躬行
读者只有自己一步一步的跟着做,才能真正学会,看是看不会的
平衡有序二叉树的构建
平衡二叉树
整棵树任意一个节点的左右子树高度差值的绝对值<=1(高度相等或相差1)
demo1
根节点3不平衡,左子树高度为2,右子树高度为0,相差2>1
demo2
平衡,所有结点都满足左右子树高度差<=1的特性
有序二叉树
整个树的所有结点满足左孩子结点数值<根节点<右孩子结点数值,有序二叉树的中序遍历就恰好是数值从小到大的排列顺序
demo
所有结点都满足,左孩子比自己小,右孩子比自己大的特性
中序遍历(左根右)
3,7,21,34,78,322(从小到大)
平衡有序二叉树
上述两者的特性都满足的二叉树
为什么会出现平衡有序二叉树,有序二叉树不就足够了吗
有序二叉树的性质确实是非常好
①构建完成之后自动就是排好序的状态,不需要我们额外的操作进行排序(哪怕是最快的排序时间复杂度也得至少O(nlogn))
②查询可以使用折半查找,仅需要O(logn)的时间复杂度即可
③增删改都不复杂(O(logn)级别),不需要额外的申请空间
但是以上情况只出现在有序二叉树比较完美的情况下
如果是这种有序二叉树
有序二叉树就会退化为有序链表,CURD的效率都会退化为O(n)级别
所以我们才需要平衡有序二叉树
构建
平衡有序二叉树的结点的插入,如果导致不满足平衡二叉树的特性,就要进行树的旋转
我在这里非常不建议读者去乱搞什么左旋右旋,很容易旋晕,我们只需要记住怎么转变即可
通用的方法论(LL型,RR型,LR型,RL型)
4个步骤,2点注意
4个步骤
- 插入后,不平衡的结点向造成不平衡的结点走两步,根据这两步确定不平衡的类型
- 把无关结点(需要改变的三个结点当中的中间结点的兄弟结点)去掉,这些结点等待后续重新插入
- 三个结点的改变
- 最后重新插入刚才去掉的结点
2点注意
- 若一个结点的插入,同时导致了两个结点的不平衡,优先解决离叶子节点近的结点
- 若两步走之后,不平衡结点与造成不平衡的结点不能完全相连,那么将造成不平衡的结点与其父节点看成一个整体
将18和19看成一个整体
看不懂?我们直接实战
LL型
demo1
直接套我们的方法论
-
插入后,不平衡的结点向造成不平衡的结点走两步,根据这两步确定不平衡的类型
3是造成不平衡的结点,5是不平衡结点(左子树高度2-右子树高度0 = 2>1),不平衡的结点 向造成不平衡的结点走两步,我们发现都是向左走,确定类型:LL型 -
把无关结点(需要改变的三个结点当中的中间结点的兄弟结点)去掉,这些结点等待后续重新插入
这里比较幸运,没有那么多其他结点,我们不需要去除结点,当然第四步也不需要重新插入 -
三个结点的改变
不平衡结点5顺时针旋转,成为4的右孩子
-
最后重新插入剩余结点
这里不涉及复杂情况,我们现在看一种比较复杂的LL型
demo2
11的插入同时导致了13和15的不平衡,我们优先解决13(注意点1)
-
插入后,不平衡的结点向造成不平衡的结点走两步,根据这两步确定不平衡的类型
11是造成不平衡的结点,13是不平衡结点(左子树高度2-右子树高度0 = 2>1),不平衡的结点向造成不平衡的结点走两步,我们发现都是向左走,确定类型:LL型
-
把无关结点(需要改变的三个结点之中的后两个结点的左右子树)去掉,这些结点等待后续重新插入
后两个结点指的是11和12,这两个结点都没有左右孩子,所以没有结点需要暂时删除
-
三个结点的改变
不平衡结点13顺时针旋转,成为12的右孩子
- 最后重新插入剩余结点
RR型
demo1
直接套我们的方法论
-
插入后,不平衡的结点向造成不平衡的结点走两步,根据这两步确定不平衡的类型
19是造成不平衡的结点,15是不平衡结点(右子树高度3-左子树高度1 = 2>1),不平衡的结点向造成不平衡的结点走两步,我们发现都是向右走,确定类型:RR型
-
把无关结点(需要改变的三个结点当中的中间结点的兄弟结点)去掉,这些结点等待后续重新插入
去掉16,同时18和19看成一个整体
-
三个结点的改变
不平衡结点15逆时针旋转,成为17的左孩子
-
最后重新插入刚才去掉的结点
LR型
demo1
直接套我们的方法论
-
插入后,不平衡的结点向造成不平衡的结点走两步,根据这两步确定不平衡的类型
15是造成不平衡的结点,16,17是不平衡结点,优先16,不平衡的结点向造成不平衡的结点走两步,先左后右,确定类型:LR型
-
把无关结点(需要改变的三个结点当中的中间结点的兄弟结点)去掉,这些结点等待后续重新插入
这里比较幸运,没有那么多其他结点,我们不需要去除结点,当然第四步也不需要重新插入 -
三个结点的改变
15和13互换位置,变成LL型;然后16顺时针旋转,成为15的右孩子
其实就是后两个结点互换位置变成LL,然后中间节点顶上去变成父节点,非常简单 -
最后重新插入刚才去掉的结点
RL型
直接套我们的方法论
-
插入后,不平衡的结点向造成不平衡的结点走两步,根据这两步确定不平衡的类型
16是造成不平衡的结点,15是不平衡结点(左子树高度3-右子树高度1 = 2>1),不平衡的结点向造成不平衡的结点走两步,我们发现先向右后向左,确定类型:RL型
-
把无关结点(需要改变的三个结点当中的中间结点的兄弟结点)去掉,这些结点等待后续重新插入
去掉19,同时16,17看成一个整体
-
三个结点的改变
17,18互换位置变成RR型,然后17成为新的父节点,15是17的左子树
4. 最后重新插入刚才去掉的结点
终极demo
画出关键字序列{25,27,30,12,11,18,14,20,15,22,50,16,74,60,43,16,90,46,31}构造的一课平衡二叉树
这里推荐一个网站,可以动态展现这个过程
AVL Tree
我们一步一步来
这个明显是RR型,让27成为新的父节点
11的插入同时造成了25和27的不平衡,优先25;明显LL型,让12成为新的父节点
18的插入导致了27的不平衡,25和18看成一个整体;显然是LR型,首先12和25互换位置变成LL型,然后25成为新的父节点,27成为25的右子树;18重新插入
15的插入导致了12和25的不平衡,12优先;
明显RL型,14和15看成一个整体,去掉20
14和18互换位置,转成RR型,
然后14成为新的父节点
20和15重新插入
22的插入导致了25的不平衡,LR型
去掉12,15;18,19,22看成一个整体;
18,14互换位置变成LL型
18成为新的父节点
剩余结点重新插入
50的插入导致了25的不平衡
RR型,省略步骤
60的插入同时导致了50,30,18的不平衡
RL型,
总结
平衡有序二叉树的构建,相当的复杂,非常耗费计算机的cpu资源
我们需要找出一种更为简单的,构建平衡有序二叉树的方式——红黑树