1.什么是AVL树
有了经典的二叉搜索树做铺垫,我们就可以正式开始讲解由它衍生出的几种改进的二叉搜索树结构。
首先是AVL树,也就是我们常说的平衡二叉树。
从名字就可以看出,AVL树是一种高度平衡的二叉搜索树,即任意节点的左子树和右子树高度之差都不超过1。
那么我们为什么要让一棵二叉搜索树尽量平衡呢?
我们假设现在有一组输入数据[1,2,3,4,5],按照先前的插入逻辑,就会形成如下结构的二叉树:
这棵树的高度和它的元素个数是一致的,因为二叉搜索树各种操作所消耗的时间与其高度密切相关,因此这棵树所形成的的结构显然不是我们想看到的。
那么如何减小树的高度呢?答案是尽量让这棵树左右平衡。这里就要引入平衡因子的概念:二叉树上节点的左子树深度减去右子树深度的值称为平衡因子。
那么根据平衡二叉树的定义,平衡二叉树上的所有节点的平衡因子只可能是-1、0、1,否则就不是平衡二叉树。
2.AVL树的实现原理
当一棵二叉搜索树进行增加或删除操作时,就有可能产生不平衡的情况。我们要做的就是在出现不平衡的状况时及时进行调整,让整棵树恢复平衡。而用于调整的手段,就是之前介绍的左旋和右旋操作。
首先我们需要明确,在什么时候、如何进行左旋或右旋操作,这就需要分情况讨论:
情况一:LL
上图中所示的三种不平衡的情况,都是根节点的左孩子的左孩子(LL)所造成,即因为LL本身或LL身上挂载了其他节点,造成了根节点的不平衡。这种情况下我们只需要对根节点进行一次右旋操作,即可恢复平衡。
调整后如下图:
在进行LL调整后:
原本的根节点和根节点的左孩子节点的平衡因子会归零;
原LL节点的平衡因子不会发生改变。
情况二:RR
与LL相反,以上三种不平衡的情况都是根节点的右孩子的右孩子(RR)所造成,即因为RR本身或RR身上挂载了其他节点,造成了根节点的不平衡。这种情况下我们只需要对根节点进行一次左旋操作,即可恢复平衡。
调整后如下图:
在进行RR调整后:
原本的根节点和根节点的右孩子节点的平衡因子会归零;
原RR节点的平衡因子不会发生改变。
情况三:LR
这种不平衡的情况是根节点的左孩子的右孩子(LR)所造成,即因为LR本身或LR身上挂载了其他节点,造成了根节点不平衡。
与前两种情况不同的是,上图所示的三种情况无法通过单次的左旋或右旋操作使其达到平衡。以第一种情况为例,假如我们直接对根节点进行右旋操作,会发现3节点成为了4、6节点的父节点,这显然不符合二叉搜索树的规则。
究其原因,是因为根节点的平衡因子与根节点的左孩子的平衡因子符号不相同导致。那么我们只需要先将符号统一,再进行整体上的旋转操作即可。
做法是先将左子树进行左旋,再对整体进行右旋,调整过程如下:
在进行LR调整后:
原LR节点的平衡因子都会归零;
如果原LR节点平衡因子为-1,则调整后的原L节点平衡因子为1,否则为0;
如果原LR节点平衡因子为1,则调整后的原根节点平衡因子为-1,否则为0;
情况四:RL
这种不平衡的情况是根节点的右孩子的左孩子(RL)所造成,即因为RL本身或RL身上挂载了其他节点,造成了根节点不平衡。
这种情况的调整方法与情况三相对,这里不再赘述,调整过程如下:
在进行RL调整后:
原RL节点的平衡因子都会归零;
如果原RL节点平衡因子为1,则调整后的原R节点平衡因子为-1,否则为0;
如果原RL节点平衡因子为-1,则调整后的原根节点平衡因子为1,否则为0;
3.代码实现
因为AVL树本质上是对二叉搜索树的扩展,因此在代码层面,我们只需要继承之前的BSTree类,并重写afterAdd、createNode方法即可:
/**
* AVL树节点类
*/
public class AVLTreeNode extends BSTreeNode{
/**
* 平衡因子
*/
public int height;
/**
* 构造方法
* @param data
*/
public AVLTreeNode(int data){
super(data);
height = 0;
}
}
/**
* AVL树
*/
public class AVLTree extends BSTree{
/**
* 构造方法
* @param data
*/
public AVLTree(int data) {
super(data);
}
/**
* 创建一个节点
* @param data
* @return
*/
@Override
protected BSTreeNode createNode(int data) {
return new AVLTreeNode(data);
}
/**
* 新增成功后触发
* @param node
*/
@Override
protected void afterAdd(BSTreeNode node) {
AVLTreeNode cur = (AVLTreeNode) node;
AVLTreeNode parent = (AVLTreeNode) cur.parent;
if(parent == null){
return;
}
while(parent != null){
// 调整父节点的平衡因子
if(cur == parent.left){
parent.height++;
}else{
parent.height--;
}
// 如果平衡因子为0,则无需继续调整
if(parent.height == 0){
break;
}else if(Math.abs(parent.height) == 1){
// 如果平衡因子是1或-1,继续向上调整
cur = parent;
parent = (AVLTreeNode) parent.parent;
}else{
// 如果平衡因子是2或-2,则已经不平衡,需要进行旋转操作
if(parent.height == -2){
AVLTreeNode R = (AVLTreeNode) parent.right;
AVLTreeNode RL = (AVLTreeNode) parent.right.left;
// RL型
if(R.height == 1){
// 对右子树进行一次右旋
rightRotate(R);
// 对根节点进行一次左旋
leftRotate(parent);
// 调整平衡因子
if(RL.height == 1){
R.height = -1;
parent.height = RL.height = 0;
}else if(RL.height == -1){
parent.height = 1;
R.height = RL.height = 0;
}else{
RL.height = R.height = parent.height = 0;
}
}else{
// RR型,对根节点进行一次左旋
leftRotate(parent);
// 调整平衡因子
parent.height = R.height = 0;
}
}else{
AVLTreeNode L = (AVLTreeNode) parent.left;
AVLTreeNode LR = (AVLTreeNode) parent.left.right;
// LR型
if(L.height == -1){
// 对左子树进行一次左旋
leftRotate(L);
// 对根节点进行一次右旋
rightRotate(parent);
// 调整平衡因子
if(LR.height == -1){
L.height = 1;
parent.height = LR.height = 0;
}else if(LR.height == 1){
parent.height = -1;
LR.height = L.height = 0;
}else{
LR.height = L.height = parent.height = 0;
}
}else{
// LL型,对根节点进行一次右旋
rightRotate(parent);
// 调整平衡因子
parent.height = L.height = 0;
}
}
break;
}
}
}
}