AVL树的概念:
AVL树是二叉查找树的扩展之一, 它的出现是为了弥补二叉搜索树的不足。
二叉搜索树的缺点:
二叉搜索树虽可以缩短查找的效率,但如果数据有序或接近有序二叉搜索树将退化为单支树,查找元素相当于在顺序表中搜索元素,效率低下。
因此,两位俄罗斯的数学家G.M.Adelson-Velskii和E.M.Landis在1962年 发明了一种 解决上述问题的方法:当向二叉搜索树中插入新结点后,如果能保证每个结点的左右子树高度之差的绝对值不超过 1,即可降低树的高度,从而减少平均搜索长度.
一棵AVL树或者是空树或者是具有以下性质的二叉搜索树:
它的左右子树都是AVL树
左右子树高度之差(简称平衡因子)的绝对值不超过1(-1/0/1)
如果一棵二叉搜索树是高度平衡的,它就是AVL树。如果它有n个结点,其高度可保持在 ,搜索时间复杂在log以2为底的n。
AVL树节点的定义
代码演示
class AVLTreeNode {
public AVLTreeNode left = null; // 节点的左孩子
public AVLTreeNode right = null; // 节点的右孩子
public AVLTreeNode parent = null; // 节点的双亲
public int val = 0;
public int bf = 0; // 当前节点的平衡因子=右子树高度-左子树的高度
public AVLTreeNode(int val) {
this.val = val;
}
}
AVL树的插入
AVL树就是在二叉搜索树的基础上引入了平衡因子,因此AVL树也可以看成是二叉搜索树。所以AVL树的插入过程可以分为两步
- 按照原二叉搜索树的方式插入新节点
- 调整节点的平衡因子
这里我们重点探究”调整节点的平衡因子”
AVL树的旋转
如果在一棵原本是平衡的AVL树中插入一个新节点,可能造成不平衡,此时必须调整树的结构,使之平衡化。根据 节点插入位置的不同,AVL树的旋转分为四种
新节点插入较高左子树的左侧---右单旋
在插入元素4(subL)前, AVL树是平衡的, 新节点插入,导元素5(parent)的二叉树不平衡,因此需要平衡元素5(parent)的二叉树,要让元素5(parent)平衡,只能将元素5(parent)的左子树减一层,右子树增加一层,想要达到此效果,只好将左子树往上提,这样5(parent)转下来,因为元素5(parent)比元素4(subL)大,只能将其放元素4(subL)的右子树,旋转完成后,更新节点的平衡因子即可。
而如果subL有右子树,右子树根的值 一定大于subL,小于parent只能将其放在parent的左子树
在旋转过程中 ,有以下几种情况需要考虑:
- subL节点的右子树可能存在,也可能不存在
- parent可能是根节点,也能是子节点, 如果是根节点,旋转完成后,需要更新根节点, 如是子树, 可能是某个节点的左子树, 也可能是左子树,因此需要更新相应节点.
代码实现
public void rotaRight(TreeNode parent){
TreeNode subL = parent.left;
TreeNode subLR = subL.right;
parent.left = subL.right;
subL.right = parent;
if (subLR != null){
subLR.parent = parent;
}
TreeNode Pparent = parent.parent;
parent.parent =subL;
if (Pparent == null){
root = subL;
root.parent =null;
}else {
if (Pparent.right == parent){
Pparent.right = subL;
}else {
Pparent.left = subL;
}
subL.parent =Pparent;
}
subL.df = 0;
parent.df = 0;
}
新节点插入较高右子树的右侧---左单旋
实现与右单旋差不多,需要改变参数对应
新节点插入导致, AVL树失去平衡,所有需要对AVL树旋转,将subL往上提,parent插入到subL左侧成为左子树,之后调整平衡因子.
在旋转过程中 ,有以下几种情况需要考虑:
subL节点的左子树可能存在,也可能不存在
parent可能是根节点,也能是子节点, 如果是根节点,旋转完成后,需要更新根节点, 如是子树, 可能是某个节点的左子树, 也可能是左子树,因此需要更新相应节点.
代码实现
public void rotaLeft(TreeNode parent){
TreeNode subL = parent.right;
TreeNode subLR = subL.left;
subL.left = parent;
parent.right = subLR;
if (subLR != null){
subLR.parent = parent;
}
TreeNode Pparent = parent.parent;
parent.parent = subL;
if (Pparent == null){
root = subL;
root.parent = null;
}else {
if (Pparent.right == parent){
Pparent.right = subL;
}else {
Pparent.left = subL;
}
subL.parent = Pparent;
}
subL.df=0;
parent.df = 0;
}
新节点插入较高左子树的右侧的左右:先左单旋再右单旋【左右双旋】
新节点插入较高左子树的右侧的左右,导致AVL失去平衡,通简单的右旋无法使AVL恢复平衡,想达到平衡先对的AVL树进行左单旋再右单旋
左右双旋具体步骤:
左单旋:将sublR节点往上提,subL插入到sublR的左节点中;
右单旋:在左旋完后,对sublR进行右单旋, 将sublR往上提, 由于sublR的右节点数值一定小于parent,所以将sublR的右子树插入到parent的左子树,parent插入为sublR的右子树, 最后调整平衡因子.
新节点插入较高右子树的左侧的右左:先右单旋再左单旋【右左双旋】
新节点插入较高左子树的右侧的左右,导致AVL失去平衡,通简单的右旋无法使AVL恢复平衡,想达到平衡先对的AVL树进行左单旋再右单旋
右左双旋具体步骤:
右单旋: 将sublR节点往上提,subL插入到sublR的右节点中;
左单旋: 在右旋完后,对sublR进行左单旋, 将sublR往上提, 由于sublR的左节点数值一定大 于 parent,所以将sublR的左子树插入到parent的右子树,parent插入为sublR的左子树, 最后调整平衡因子
旋转总结:
在AVL树中插入元素, 有四种方式会导致AVL树失去平衡,总计如下
好了本次的学习分享就到这里了,如果本次分享对你有帮助的话,请点一个免费的赞支持一下作者哦.谢谢啦! 我们下次再见.