avl树,又叫平衡二叉搜索树,是在二叉搜索树的基础上建立的一种树,它相比bst,多了一个特性,就是任意一棵子树根节点的左右子树的高度差的绝对值不超过1,这是为了防止bst中最坏情况的出现,一棵avl树的深度为o(logN)。
对于节点的插入,有可能会导致树平衡条件被打破,因此,在插入时,需要对树进行结构调整,以重新满足平衡条件。
我们假设某棵子树的根节点为r,导致r的平衡条件被打破的可能性有以下四种:
1、往r的左儿子的左子树进行插入
2、往r的右儿子的右子树进行插入
3、往r的左儿子的右子树进行插入
4、往r的右儿子的左子树进行插入
可以看出,1与2是对称类似的,只需要进行一次旋转就行,3与4同样对称类似,但是需要进行两次旋转。
一、但旋转
对于1和2这两种情况,我们讨论第1种,也就是对r的左儿子的左子树进行插入操作
[img]http://dl.iteye.com/upload/attachment/605320/7778829c-2ed1-3bd8-aab1-ddf6b1ed5a9f.jpg[/img]
[align=center]图 1[/align]
由图1可以看出,当插入到子树X中时,导致k2的左子树比右子树的高度高2,导致不平衡,需要进行旋转,将k1和k2进行旋转,然后把k1的子树Y挂接为k2的左子树,可以看出,在这种情况下,只需要进行一次旋转就行了。
我们注意到,在调整的时候,k1和k2的子树也发生了变化,Y变成了k2的左子树,的确,因为k2变成了k1的右子树,导致k1变成了三叉树,所以必须对k1的某棵子树重新调整父节点,考虑到bst的特点,发现Y中的节点树都大于k1,同时也小于k2,而此时k2的左子树原来是k1,经过调整后k2的左子树正好空缺,所以可以把k2的左子树设为Y,至此,我们完美的完成了单旋转调整。
二、双旋转
当第三种情况出现时,会略微复杂,我们先看下面这个例子。
[img]http://dl.iteye.com/upload/attachment/605403/d46e603a-a2e9-3366-a99c-186a41777fe8.jpg[/img]
[align=center]图2[/align]
很明显,进行一次单旋转操作后仍然无法满足平衡条件,因此,我们需要对Y树进行改造。将其拆为根节点r和两颗子树Y1,Y2。其中,假设是因为Y2导致树平衡条件被打破
[img]http://dl.iteye.com/upload/attachment/605425/18028a1f-bb52-3b61-ad3c-78cd02ba793a.jpg[/img]
[align=center]图3[/align]
先对k1和r进行旋转,然后在对r和k2进行旋转,就可以重新满足平衡条件了。
ok,是不是觉得很简单呢?既然思路已经理清了,那代码就可以信手拈来了,下面是基于递归的实现,avl树的平衡调整还是有些复杂的,基于递归的实现要容易理解一些,下次我会补全没有采用递归的实现。
经过测试,该实现完全正确
对于节点的插入,有可能会导致树平衡条件被打破,因此,在插入时,需要对树进行结构调整,以重新满足平衡条件。
我们假设某棵子树的根节点为r,导致r的平衡条件被打破的可能性有以下四种:
1、往r的左儿子的左子树进行插入
2、往r的右儿子的右子树进行插入
3、往r的左儿子的右子树进行插入
4、往r的右儿子的左子树进行插入
可以看出,1与2是对称类似的,只需要进行一次旋转就行,3与4同样对称类似,但是需要进行两次旋转。
一、但旋转
对于1和2这两种情况,我们讨论第1种,也就是对r的左儿子的左子树进行插入操作
[img]http://dl.iteye.com/upload/attachment/605320/7778829c-2ed1-3bd8-aab1-ddf6b1ed5a9f.jpg[/img]
[align=center]图 1[/align]
由图1可以看出,当插入到子树X中时,导致k2的左子树比右子树的高度高2,导致不平衡,需要进行旋转,将k1和k2进行旋转,然后把k1的子树Y挂接为k2的左子树,可以看出,在这种情况下,只需要进行一次旋转就行了。
我们注意到,在调整的时候,k1和k2的子树也发生了变化,Y变成了k2的左子树,的确,因为k2变成了k1的右子树,导致k1变成了三叉树,所以必须对k1的某棵子树重新调整父节点,考虑到bst的特点,发现Y中的节点树都大于k1,同时也小于k2,而此时k2的左子树原来是k1,经过调整后k2的左子树正好空缺,所以可以把k2的左子树设为Y,至此,我们完美的完成了单旋转调整。
二、双旋转
当第三种情况出现时,会略微复杂,我们先看下面这个例子。
[img]http://dl.iteye.com/upload/attachment/605403/d46e603a-a2e9-3366-a99c-186a41777fe8.jpg[/img]
[align=center]图2[/align]
很明显,进行一次单旋转操作后仍然无法满足平衡条件,因此,我们需要对Y树进行改造。将其拆为根节点r和两颗子树Y1,Y2。其中,假设是因为Y2导致树平衡条件被打破
[img]http://dl.iteye.com/upload/attachment/605425/18028a1f-bb52-3b61-ad3c-78cd02ba793a.jpg[/img]
[align=center]图3[/align]
先对k1和r进行旋转,然后在对r和k2进行旋转,就可以重新满足平衡条件了。
ok,是不是觉得很简单呢?既然思路已经理清了,那代码就可以信手拈来了,下面是基于递归的实现,avl树的平衡调整还是有些复杂的,基于递归的实现要容易理解一些,下次我会补全没有采用递归的实现。
package avl;
/**
* avl树的实现
*
* @author 梅东
*
* @param <E>
*/
public class AvlTree<E extends Comparable<? super E>> {
private AvlNode<E> root;
public void insert(E e) {
this.root = insert(e, root);
}
public int height(AvlNode<E> r) {
if (r == null)
return -1;
else
return r.height;
}
/**
* 前序输出每个节点的值
*/
@Override
public String toString() {
StringBuilder result = new StringBuilder();
return DLR(result, this.root);
}
/**
* 插入节点
*
* @param e
* @param r
* @return
*/
public AvlNode<E> insert(E e, AvlNode<E> r) {
// 根节点为空,返回一个新节点
if (r == null)
return new AvlNode<E>(e);
// 该节点和根节点的比较结果
int compareResult = e.compareTo(r.element);
// 小于0,说明是插入到根节点的左子树中去了
if (compareResult < 0) {
r.leftChild = insert(e, r.leftChild);
// 左子树与右子树的高相差2,打破了平衡
if (height(r.leftChild) - height(r.rightChild) == 2) {
if (e.compareTo(r.leftChild.element) < 0)
// 插入到左子树根节点的左子树中,是第一种情况,左旋转
r = rotateWithLeftChild(r);
else
// 插入到左子树根节点的右子树中,是第三种情况,左-右旋转
r = doubleWithLeftChild(r);
}
} else if (compareResult > 0) {
// 大于0,说明是插入到根节点的右子树中去了
r.rightChild = insert(e, r.rightChild);
// 右子树与左子树高度差为2,打破了平衡
if (height(r.rightChild) - height(r.leftChild) == 2) {
if (e.compareTo(r.rightChild.element) > 0)
// 插入到了右子树根节点的右子树中区,第二种情况,右旋转
r = rotateWithRightChild(r);
else
// 插入到右子树根节点的左子树中,是第四种情况,右-左旋转
r = doubleWithRightChild(r);
}
} else
;
r.height = Math.max(height(r.leftChild), height(r.rightChild)) + 1;
return r;
}
/**
* 左旋转
*
* @param r
* @return
*/
private AvlNode<E> rotateWithLeftChild(AvlNode<E> r) {
AvlNode<E> root = r.leftChild;
r.leftChild = root.rightChild;
root.rightChild = r;
r.height = Math.max(height(r.leftChild), height(r.rightChild)) + 1;
root.height = Math.max(height(root.leftChild), r.height) + 1;
return root;
}
/**
* 右旋转
*
* @param r
* @return
*/
private AvlNode<E> rotateWithRightChild(AvlNode<E> r) {
AvlNode<E> root = r.rightChild;
r.rightChild = root.leftChild;
root.leftChild = r;
r.height = Math.max(height(r.leftChild), height(r.rightChild)) + 1;
root.height = Math.max(height(root.rightChild), r.height) + 1;
return root;
}
/**
* 左-右旋转
*
* @param r
* @return
*/
private AvlNode<E> doubleWithLeftChild(AvlNode<E> r) {
r.leftChild = rotateWithRightChild(r.leftChild);
return rotateWithLeftChild(r);
}
/**
* 右-左旋转
*
* @param r
* @return
*/
private AvlNode<E> doubleWithRightChild(AvlNode<E> r) {
r.rightChild = rotateWithLeftChild(r.rightChild);
return rotateWithRightChild(r);
}
/**
* 前序遍历
*
* @param result
* @param root
* @return
*/
private String DLR(StringBuilder result, AvlNode<E> root) {
if (root != null) {
result.append(root.element);
result.append(" ");
DLR(result, root.leftChild);
DLR(result, root.rightChild);
}
return result.toString();
}
/**
* 树节点实现
*
* @author 梅东
*
* @param <E>
*/
private static class AvlNode<E> {
E element;
AvlNode<E> leftChild;
AvlNode<E> rightChild;
int height;
public AvlNode(E element) {
this(element, null, null);
}
public AvlNode(E element, AvlNode<E> leftChild, AvlNode<E> rightChild) {
this.element = element;
this.leftChild = leftChild;
this.rightChild = rightChild;
this.height = 0;
}
}
}
经过测试,该实现完全正确