AVL树是一种平衡二叉树,其每个节点的左右子树高度最多差1,空树高度定为-1,当左右的高度超过1,即失去了平衡,不是AVL树了。
private static class AVLNode<AnyType>{
AVLNode (AnyType element)
{this(element ,null,null);}
AVLNode(AnyTape element,AVLNode<AnyType> left,AVLNode<AnyType> right){
this.element = element;
this.left = left;
this.right = right;
}
AnyTape element;
AVLNode<AnyType> left;
AVLNode<AnyType> right;
int height;
}
这是AVL树的节点声明,声明了节点,左右子树以及高度。
在进行插入和删除操作时可能会使AVL树左右子树的高度相差超过1,破坏了平衡,当平衡破坏时,在插入或删除完成前恢复平衡要进行旋转。旋转分为单旋转和双旋转。把必须重新平衡的节点叫做t,下面是单旋转和双旋转的情况。
1.对于t的左儿子的左子树进行插入->单旋转
2.对于t的左儿子的右子树进行插入->双旋转
3.对于t的右儿子的左子树进行插入->双旋转
4.对于t的右儿子的右子树进行插入->单旋转
由此总结,左-左,右-右是单旋转,左-右,右-左是双旋转
在旋转之前,插一下节点高度计算
private int height(AVLNode<AnyType> t){
return t == null ? -1 : t.height;
}
1.左-左:单旋,右旋
private AVLNode<AnyType> rotateRight(AVLNode<AnyType> k2){
AVLNode<AnyType> k1 = k2.left;
k2.left = k1.right;
k2.height = Maht.max(height(k2.left),height(k2.right))+1;
k1.height = Maht.max(height(k1.left),k2.height)+1;
return k1;
}
2.左-右:双旋转,先左后右
private AVLNode<AnyType> doubleRight(AVLNode<AnyType> k3){
k3.left = rotateLeft(k3.left);
return rotateRight(k3);
}
3.右-左:双旋转,先右后左
private AVLNode<AnyType> doubleLeft(AVLNode<AnyType> k3){
k3.right = rotateRightk3.right);
return rotateLeft(k3);
}
4.右-右:单旋,左旋
private AVLNode<AnyType> rotateLeft(AVLNode<AnyType> k2){
AVLNode<AnyType> k1 = k2.left;
k2.left = k1.right;
k2.height = Maht.max(height(k2.left),height(k2.right))+1;
k1.height = Maht.max(height(k1.left),k2.height)+1;
return k1;
}
说完了旋转,接下来就是插入操作了
private AVLNode<AnyType> insert(AnyType x,AVLNode<AnyType>t){
if(t == null)
return new AVLNode<>(x,null,null);
int result = x.compareTo(t.element);
if( result < 0) //如果X小于节点值,则在左子树插入,递归更新
t.left = insert(x,t.left);
else if( result > 0) //如果X大于节点值,则在右子树插入,递归
t.right = insert(x,t.right);
return balance(t);
}
private static final int balance_num = 1;
private AVLNode<AnyType> balance(AVLNode<AnyType>t){
if(t == null)
return t;
if(height(t.left) - height(right) > balance_num){
if(height(t.left.left) > height(t.left.right)) //左-左
t = rotateRight(t);
else
t = doubleRight(t); //左-右
}else{
if(height(t.right.right) > height(t.right.left)) //右-左
t = rotateLeft(t);
else
t = doubleLeft(t); //右-左
}
t.height = Math.max(height(t.left),height(t.right))+1;
return t;
}
旋转一定要保持平衡,所以要返回的是balance(t)而不是t。
接下来进行删除操作,删除较插入更为复杂。
private AVLNode<AnyType> remove(AnyType x,AVLNode<AnyType>t){
if(t == null)
return new AVLNode<>(x,null,null);
int result = x.compareTo(t.element);
if( result < 0) //如果X小于节点值,则在左子树删除,递归更新
t.left = insert(x,t.left);
else if( result > 0) //如果X大于节点值,则在右子树删除,递归
t.right = insert(x,t.right);
else if(t.left != null && t.right != null){
t.element = findMin(t.right).element; //找到右子树最小值
t.right = remove(t.element,t.right) //删除右子树最小值原来的位置
}
else
t = (t.left != null)?t.left:t.right; //如果是一个儿子或者没有,哪个儿子不为空就返回哪个儿子
return balance(t);
}
总体来说删除操作和二叉搜索树的想法包括代码都几乎是一致的,但是重要不同的一点就是AVL树需要进行旋转重平衡。理解了旋转就基本上插入删除思路想法和二叉搜索都一致。
最后说明部分图片是我搜的,如果侵权或者有问题,在此向您道歉,并删除照片。