逻辑结构.树.AVL树

274 阅读3分钟

废话不多说,直接开始:)

AVL树也是一种自平衡的BST,相比起红黑树,其维护平衡的限制条件相对较为简单:==对于树上的任意结点,左右子树的高度差不超过1==。

AVL tree 60处的子树不平衡(左右高度差=2)

对于AVL树,维护平衡的主要手段就是旋转,讲红黑树的时候提到过,不再赘述。

自平衡

AVL树无论是插入还是删除,都是BST插入/删除 + 自平衡。所以关键点就在于平衡的维护。为了尽量减小维护平衡的开销,所以平衡的维护是对于最小的不平衡子树,也就是自底向上找到第一个不平衡的点维护。==自底向上==的平衡操作会有4大类情况。

left-left case

left-right case

先左旋转换成left-left case,再用left-left case的方式解决

right-right case

从这里开始都是上边的镜像情况。

right-left case

可以发现,总体的规律都是转换成倾斜到一条线上的情况,再进行旋转。

这里主要展示一下维护平衡的代码,具体的完整AVL树代码可以戳这里

// 高度差值:左子树 - 右子树
private int balaneceFactor(Node x) {
    return height(x.left) - height(x.right);
}

// 自平衡操作,返回平衡后的树
private Node balance(Node x) {
    if (x == null) return null;
    int factor = balanceFactor(x);
    if (factor < -1) { // right cases
        if (balanceFactor(x.right) > 0) { // right-left
            x.right = rotateRight(x.right);
        }
        x = rotateLeft(x); // right-right
    } 
    else if (factor > 1) { // left cases
        if (balanceFactor(x.left) < 0) { // left-right
            x.left = rotateLeft(x.left);
        }
        x = rotateRight(x); // left-left
    }
}

// 返回左旋之后的树
private Node rotateLeft(Node x) {
    final Node y = x.right; // 左旋需要将x的右孩子旋转上来
    x.right = y.left;
    y.left = x;
    x.height = 1 + Math.max(height(x.left), height(x.right));
    y.height = 1 + Math.max(height(y.left), height(y.right));
    return y;
}

// 返回左旋之后的树
private Node rotateRight(Node x) {
    final Node y = x.left;	// 右旋需要将x的左孩子旋转上来
    x.left = y.right;
    y.right = x;
    x.height = 1 + Math.max(height(x.left), height(x.right));
    y.height = 1 + Math.max(height(y.left), height(y.right));
    return y;
}

红黑树 vs AVL树

从红黑树和AVL树的约束性质可以看出一个比较明显的区别:==AVL树的平衡性比红黑树更好==。因为在较差的情况下,例如插入的值是逐渐递增的,那么红黑树也是会产生一定程度的右倾,例如顺序插入{1, 2, 3, 4, 5, 6, 7}

红黑树 AVL树

由此可见,红黑树只是在一定程度上缓和了bst退化成链表的问题,而AVL树则是更加有力地解决了这种问题。

但也因此,AVL树在插入和删除的时候==触发旋转的可能性就更高==,因此会有更多的开销花费在维护平衡上。这就是所谓的鱼和熊掌不可兼得。所以:

  • 对于有频繁插入、删除的情况,红黑树是更优的选择;
  • 对于查找更加频繁的情况,AVL树是更优的选择。

总结一下

这里主要聊了聊AVL树,AVL树是一种自平衡的二叉搜索树(bst),其维护平衡的规则是:每一个结点的左右子树高度差不大于1。接着其维护平衡的主要手段就是自旋,自旋总是把各种情况统一转换成往某一边线性倾斜的情况处理(left-left或者是right-right)。

之后我们将AVL树和红黑树进行了一下对比。两者都是比较重要的自平衡bst,添加、删除操作都是bst的原生操作外加上各自的自平衡操作。AVL树有力地解决了bst退化成链表的问题,而红黑树只是缓和了这种问题。故AVL树花在平衡上的开销会比红黑树多,那么在结构频繁更新变动的情况下红黑树可能更优,而对于频繁地查找AVL树会略胜一筹。

部分图片来源:geeksforgeeks.org