有了之前的BST,我们再其基础上,进行小小的改动即可形成平衡二叉树。
先看二叉平衡树的定义:
具有以下性质:它是一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。
要实现它,
首先我们需要为节点,entry新增加一个属性,表示节点的高度,int high。
之后,由于要对树进行自底向上的回溯,检查平衡因子是否符合标准。且我们不存在父节点,故需要一个辅助栈记录我们插入的节点,以便回溯时检查平衡因子。且这个辅助栈是再tree本类中的。
那么,我们新增以下功能:
左旋,右旋,先左后右旋,先右后左旋。
获取高度:
检查平衡因子
插入后恢复函数
删除后回复函数
获取节点的高度 getHight:参数:节点,返回值,高度
因为节点自带高度,故直接返回此节点的高度即可,
检查平衡因子:入参:节点,返回值:平衡因子,
Return Math.abs( getHight(p.left)-getHight(p.right) ).
左旋:
如图,
插入红色节点之后,顶级节点(笑脸节点)的平衡因子大于等于二,且右子树的深度过大,故需要左旋(笑脸节点的视角):
先获得笑脸节点的右侧节点–>绿色的花草节点,再将花草节点的左侧节点–>彩色(三色节点)赋值给笑脸节点的右侧节点,再将花草节点的左侧节点赋值给笑脸节点
如此一来,左旋完成。
右旋(同左旋一致):
第一步:
第二步:
完成。
RL操作:
进行一次右节点的右旋,在对本节点进行一次左旋即可完成。
LR同RL同理:
进行一次左节点的左旋,再对本节点进行一次右旋操作即可:
最后是检查平衡因子的函数:
FixAfterInsertion();入参为插入节点的key,因为要知道插入左子树还是右子树。
当回溯的辅助栈不为空时:弹栈,令节点的高度变成(左右子树高度的最大者+1),同时,计算平衡因子,左右子树高度的差,若是(假设为节点p)平衡因子的绝对值大于等于2,则进行旋转调平。
分为以下几种情况:
当 左子树高度-右子树高度的差 时:
平衡因子为-2则说明插入的是p节点的右子树,
继续判断插入key值与p节点右子树的key值的权重大小
若大于:表示应该为:左旋操作,执行左旋,并令p的右子树等于左旋的返回值。
若小于,则表示应该先右旋再左旋,执行RL操作,并令p的右子树等于RL的返回值。
平衡因子为2则说明插入的是p节点的左子树
继续判断插入key值与p节点左子树的key值的权重大小
若小于:表示应该为:右旋操作,执行右旋,并令p的左子树等于右旋的返回值。
若大于,则表示应该为:执行LR操作,并令p的左子树等于LR的返回值。
以上,我们就完成了平衡树的增加操作了,同时在增加时记录增加的路径,再增加节点后调用本方法即可。
接下来是删除操作后的回复方法。
写一个fixAfterDeletetion方法,入参为节点实例,
自顶向下检查,若发现平衡因子大于二(此处我的计算方式为右子树高度减左子树高度…)
当因子为2时,表明删除的是左子树,
当右子树的平衡因子大于等于0时,需要左旋
当右子树的平衡因子小于0时,需要进行RL旋转。
当因子为-2时,表明删除的是右子树,
当左子树的平衡因子小于0时,需要右旋
当左子树的平衡因子大于等于0时,需要进行LR旋转。
再删除节点后调用本方法即可。
总体的代码如下:
AVLTREE:
public class AVLTree<K,V> implements Iterable<Entry<K,V>>{
private static final int MAX = 16;
private Entry<K,V> root;
private Comparator<K> comparator;
private int size;
private LinkedList<Entry<K,V>> stack;
// TreeMap
public AVLTree() {
size = 0;
stack = new LinkedList<>();
}
public AVLTree(Comparator<K> comparator) {
this.comparator = comparator;
size = 0;