您的位置:首页 > 理论基础 > 数据结构算法

算法与数据结构 其六 Avl平衡树

2017-08-15 09:28 260 查看

Avl平衡树

接下来要介绍的树都是从二叉树演变而来的,Avl平衡树当然不例外。

何谓平衡?

原目标是树的所有左右子数的高度都相等,但是实际操作起来不现实,所以给的平衡条件是

树的每个节点的左子树和右子树的高度最多差1

那么Avl平衡树就是树的每个节点的左子树和右子树的高度最多差1 的二叉查找树。由于一科满二叉树的高度是O(logN),平衡二叉树的高度仅仅稍微大于 O(logN),所以对于我们 CRUD速度是很快的。但是在操作中有一点需要注意:

为了保持树的平衡,每次增删时判断树时对树的平衡加以控制


所以Avl树比普通二叉查找树多了一部,就是保持平衡,我们称它为旋转

旋转分为两种类型:单旋转(左旋转,单右旋转);双旋转(先左旋后右旋,先右旋后左旋)。后者是建立在前者之上。下面讲讲旋转

单旋转(右旋转)



当树的左右子树高度相差2的时候,我们将 K1的左子树挂在K2的左子树,而K1称为这个子树的根节点,保持了平衡

实际例子



这样,非常简单明了。(左旋转和这里类似,所以不做介绍)

双旋转(先右后左)

由于会出现单旋转不能解决的类型,例如下面



那么我们需要使用双旋转进行修复



实际例子



只要理解了这个概念就可以了,因为其他的是和查找二叉树

使用java代码实现AVL树

因为无论插入还是删除都可能对平衡造成影响,所以我们要记录下树的高度,用来判断是否需要旋转

//这是树的节点,height是节点下树的高度
private static class AvlNode<E>{
public AvlNode(E theElement){
this(theElement,null,null);
}

public AvlNode(E theElement, AvlNode<E> lt, AvlNode<E> rt) {
this.theElement = theElement;
this.lt = lt;
this.rt = rt;
height=0;
}

E theElement;
AvlNode<E> lt;
AvlNode<E> rt;
int height;

@Override
public String toString() {
return theElement+"";
}
}


单旋转代码

private AvlNode<E> rotateWidthLeftChild(AvlNode<E> a1) {
//这里进行旋转
AvlNode<E> a2 = a1.lt;
a1.lt=a2.rt;
a2.rt = a1;
//计算旋转后两个子树的高度
a1.height = Math.max(height(a1.rt),height(a1.lt))+1;
a2.height = Math.max(height(a2.lt),a1.height)+1;
return a2;
}


双旋转代码(先右后左)

private AvlNode<E>  doubleRotateWidthLeftChild(AvlNode<E> avlNode) {
//右旋转
avlNode.lt = rotateWidthRightChild(avlNode.lt);
//左旋转
AvlNode<E>  node = rotateWidthLeftChild(avlNode);
return node;
}


插入元素的代码

public boolean insert(E theElement){
root = insert(theElement,root);
return true;
}
private AvlNode<E> insert(E theElement,AvlNode<E> avlNode){

if(avlNode==null||theElement==null)
return new AvlNode<E>(theElement);

int compareResult = theElement.compareTo(avlNode.theElement);

if(compareResult<0){
avlNode.lt = insert(theElement,avlNode.lt);
if(height(avlNode.lt)-height(avlNode.rt)==2){
if(theElement.compareTo(avlNode.lt.theElement)<0){
avlNode = rotateWidthLeftChild(avlNode);
}else{
avlNode = doubleRotateWidthLeftChild(avlNode);
}
}
}else if(compareResult>0){
avlNode.rt = insert(theElement,avlNode.rt);
if(height(avlNode.rt)-height(avlNode.lt)==2){
if(theElement.compareTo(avlNode.rt.theElement)>0){
avlNode = rotateWidthRightChild(avlNode);
}else{
avlNode = doubleRotateWidthRightChild(avlNode);
}
}
}else;
avlNode.height = Math.max(height(avlNode.lt),height(avlNode.rt))+1;
return avlNode;
}


删除元素代码

private AvlNode<E> remove(E theElement, AvlNode<E> avlNode) {
if(avlNode==null||theElement==null)
return null;

int compateTo = theElement.compareTo(avlNode.theElement);
if(compateTo<0){
avlNode.lt = remove(theElement,avlNode.lt);
//删除之后进行旋转
if(height(avlNode.lt)-height(avlNode.rt)==2){
if(theElement.compareTo(avlNode.lt.theElement)<0){
avlNode = rotateWidthLeftChild(avlNode);
}else{
avlNode = doubleRotateWidthLeftChild(avlNode);
}
}
}else if(compateTo>0){
avlNode.rt = remove(theElement,avlNode.rt);
//删除之后进行旋转
if(height(avlNode.rt)-height(avlNode.lt)==2){
if(theElement.compareTo(avlNode.rt.theElement)>0){
avlNode = rotateWidthRightChild(avlNode);
}else{
avlNode = doubleRotateWidthRightChild(avlNode);
}
}
}else if(compateTo==0){
if(avlNode.lt!=null && avlNode.rt!=null){
//如果左子树的高度 大于右子树
if(height(avlNode.lt)>height(avlNode.rt)){
//找左子树最大的点
AvlNode<E> avl =findMax(avlNode.lt);
avlNode.theElement = avl.theElement;
//该保持还是要保持
avlNode.lt = remove(avl.theElement,avlNode.lt);
}else{
//如果左子树的高度 小于右子树
//找右子树最小的点
AvlNode<E> avl = findMin(avlNode.rt);
avlNode.theElement = avl.theElement;
//
avlNode.rt = remove(avl.theElement,avlNode.rt);
}
}else{
AvlNode<E> avl = avlNode;
if(avlNode.lt!=null){
//整个左子树向上移了
avlNode = avlNode.lt;
}else if(avlNode.rt!=null){
//整个右子树向上移了
avlNode = avlNode.rt;
}
avl=null;
}
}
return avlNode;
}


注意这里,因为删除和插入也是需要一定的开销,所以建议如果在数据量大,插入和删除操作很少的情况下,可以使用惰性删除(删除元素而不删除节点)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  数据结构 二叉树