AVL平衡二叉树

Scroll Down

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;
}