AVL平衡二叉树学习笔记
平衡二叉树
平衡二叉树是一种特殊的排序二叉树,它的性质如下:
一棵二叉排序树,它是一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。
正因为它有如下性质,它在大多数情况下比二叉排序树要矮的多,查找效率也高得多
平衡二叉树的实现方式有很多,比如说红黑树,AVL树,本文讲述AVL树的生成过程,插入过程和删除过程
生成一棵二叉排序树
(1)若左子树不空,则左子树上所有结点的值均小于它的根结点的值;
(2)若右子树不空,则右子树上所有结点的值均大于它的根结点的值;
(3)左、右子树也分别为二叉排序树;
(4)没有键值相等的结点。
根据二叉排序树的性质
我们可以建立一个二叉排序树
/**
* 建立一棵二叉排序树
* @param list 一个元素数组,且元素是实现Comparable接口的
*/
public BinarySortTree(T[] list)
{
super();
if (list.length==0)
return;
this.root = new BinaryNode<>(list[0]);
for (int i=1;i<list.length;i++)
{
add(root,list[i]);
}
}
这里的add方法是根据元素大小进行插入
/**
* 在某个结点下插入元素
* @param node 指定结点
* @param t 插入的元素
* @return 返回插入的那个结点
*/
public BinaryNode<T> add(BinaryNode<T> node, T t)
{
if (node==null)
return new BinaryNode<>(t);
//向左子树插入
if (node.data.compareTo(t)>0)
{
node.left = add(node.left, t);
}else if (node.data.compareTo(t)<0){
node.right = add(node.right, t);
}
return node;
}
插入的操作比较简单,就是根据要插入元素的大小,和当前结点进行比较,如果比当前结点小,去找左子树,如果比当前结点大就去找右子树。直到当前当前结点为空,就可以创建并返回。
生成一棵平衡二叉树
通过一棵排序二叉树可以生成一个平衡二叉树,因此我们就调用排序二叉树的构造方法,再加以调整即可。
public AVLTree(T[] list)
{
//先采用二叉排序树的构造方式进行创建
super(list);
//然后对AVL进行调整
this.root = downwardAdjustment(this.root);
}
向下调整
/**
* 向下调整函数
* @param node 从node开始向下调整
* @return 返回调整完的结点
*/
private BinaryNode<T> downwardAdjustment(BinaryNode<T> node)
{
if (node==null)
return null;
if (node.left==null && node.right==null)
return node;
node.left = downwardAdjustment(node.left);
node.right = downwardAdjustment(node.right);
//LL情况
if (getBalanceFactor(node)>1 && getBalanceFactor(node.left)>0)
{
node = rightRotate(node);
}else if (getBalanceFactor(node)>1 && getBalanceFactor(node.left)>1){
//LR情况
node.left=leftRotate(node.left);
node = rightRotate(node);
}else if (getBalanceFactor(node)<-1 && getBalanceFactor(node.right)<0)
{
//RR情况
node = leftRotate(node);
}else if (getBalanceFactor(node)<-1 && getBalanceFactor(node.right)>0){
//RL情况
node.right = rightRotate(node.right);
node = leftRotate(node);
}
return node;
}
向下调整的几种情况
LL
这种情况下,如果需要平衡就需要把y
作为x
的右子树,
因为这样,还能继续保持一棵排序二叉树
变成这样
// 右旋,对应LL情况
// 其中node为图中的y
private BinaryNode<T> rightRotate(BinaryNode<T> node)
{
BinaryNode<T> x = node.left;
BinaryNode<T> temp = x.right;
x.right = node;
node.left = temp;
return x;
}
RR
这种情况是LL的镜像情况,同样的,只需要y
作为x
的左子树就可以完成转换
//左旋,对应RR情况
private BinaryNode<T> leftRotate(BinaryNode<T> node)
{
BinaryNode<T> x = node.right;
BinaryNode<T> temp = x.left;
x.left = node;
node.right = temp;
return x;
}
LR
对于这种情况,我们需要去对x进行调整,暂时地改变x左右子树的位置,变成
然后再进行调整
//LR情况
node.left=leftRotate(node.left);
node = rightRotate(node);
RL
这种情况是LR情况的镜像,同样,我们需要调换x左右子树的位置,变成
然后再进行调整
//RL情况
node.right = rightRotate(node.right);
node = leftRotate(node);
插入和删除操作
知道了如何去向下调整,插入和删除就变得很容易
插入
我们按排序二叉树的方式进行插入,然后再调整一下就可以了
public void add(T t)
{
this.root = add(t, root);
}
//从node开始添加
public BinaryNode<T> add(T t,BinaryNode<T> node)
{
BinaryNode<T> add = super.add(node, t);
return downwardAdjustment(add);
}
删除
我们同样用排序二叉树的删除方式进行删除操作
//返回被删除的结点
public BinaryNode<T> remove(T t)
{
BinaryNode<T> remove = super.remove(t);
this.root = downwardAdjustment(this.root);
return remove;
}