BBST (Balance Binary Search Tree) 平衡二叉搜索树
首先了解一下随机生成和随机组成的区别:
名称 | 插入点的个数 | 可生成BST树个数 | 平均树高 |
---|---|---|---|
随机生成 | N | N! | log2N |
随机组成 | N | Catalan(N) | 根号N |
N个节点构成的二叉树,树高为log2N,称为理想平衡
N个节点构成的二叉树,树高渐进地接近log2N,称为适度平衡
适度平衡的BST称为BBST
等价变换旋转调整:Zig(右旋) Zag(左旋)
等价意味着中序遍历不变,节点之间的上下父子关系可变。
AVL (Adelson-Velsky-Landis Tree) 自适应平衡二叉搜索树
首先,我们在此处引入平衡因子
平衡因子定义为:平衡因子(v) = v的左子树高度 – v的右子树高度
AVL要满足所有节点的平衡因子不超过1也不小于-1
插入:
时间复杂度O(1)
单旋
若插入一个新的节点x后引发了部分节点的失衡,那么高度最低的失衡点g不会低于新插入节点的祖父。通过一次Zig变换或者Zag变换改变失衡节点g的状态,g恢复平衡后,x更高的祖先也将恢复平衡。不需要对其更高的祖先们再进行其他操作。
改变一个节点的失衡状态,便可以使整个树恢复平衡。
双旋
与单旋情况一样,高度最低的失衡点g不会低于新插入节点的祖父。经过两次Zig或Zag变换,使g恢复平衡。
删除:
时间复杂度O(logN)
单旋
删除单个节点x的时候,有可能首个失衡节点g就是x的父亲,此时通过一次Zig或Zag变换即可使g恢复平衡。但是此时,与插入操作不同的是,节点g的父亲以及更高的祖先可能会相继发生失衡,此时,需要逐个遍历祖先节点以判断其是否失衡,并处以相应的复恒处理即可。
改变一个节点的失衡状态,可能会引发整个树的失衡。
双旋
调用两次Zig或Zag变换即可。同样,也应逐个遍历祖先节点以判断其是否失衡,并处以相应的复恒处理即可。
说明
Zig和Zag变换方便了我们加深对AVL恢复平衡操作的理解,然而如果具体按照Zig和Zag编写代码进行左旋右旋的步骤是很复杂的。然而,令人高兴的是我们有更好的方式去实现恢复平衡的操作。在此,我们引入3+4重构的理念。可以说Zig和Zag只是辅助我们理解AVL的自适应变换,真正需要实现的代码是3+4重构。
3+4重构:
可以理解为魔方的恢复操作。我们在恢复一个被打乱的魔方时,需要经过多次的旋转才可以将魔方恢复至初态。但是,在魔方生产车间,工人们是如何做的呢?工人们可不会没事闲的一个个去转动魔方,而是将魔方的各个部件按照初态的样子进行直接组装。相应的,AVL既然是为了使树恢复平衡,我们只需要把相关的节点拆分,重新进行组装即可。
在对AVL树进行插入或删除操作时,我们需要找到最低高度的失衡节点g,并将它作为爷爷,对其祖孙三代g、p、v及各自对应的子树进行拆分和组装的操作。
具体操作:现在假设祖孙三个节点按照中序遍历的次序重命名为a、b、c,这三个节点对应的子树的根节点按照中序遍历的次序为T1、T2、T3、T4,现在我们只需按照T1、a、T2、b、T3、c 、T4的顺序重新组合成理想二叉树即可。同时不要忘记更新a、b、c三个节点的高度。(具体操作为,T1、T2作为a的左右子树,T3、T4作为c的左右子树,a、c作为b的左右子树)
此时,我们需要写出旋转函数rotate( ),其传入的参数为孙子节点v。在rotate(v)中定义其父亲节点p和祖父节点g。根据插入或者删除操作后,失衡二叉树属于的Zig-Zag类型,将g、p、v及各自对应的子树与a、b、c及T1、T2、T3、T4进行参数绑定即可完成相当于旋转的操作。