数据结构:平衡二叉树

一、核心定义

  • 本质 :二叉搜索树(BST)的优化版本,通过限制左右子树的高度差(平衡因子),避免树退化为链表,保障查询、插入、删除操作的时间复杂度稳定在 O(log n)
  • 平衡因子(BF) :节点左子树高度 - 右子树高度(部分实现为绝对值),平衡二叉树要求所有节点的平衡因子满足 |BF| ≤ 1
  • 常见类型:AVL树(严格平衡)、红黑树(近似平衡,工业界常用)、Splay树(自调整平衡)。
  • 资料:https://pan.quark.cn/s/43d906ddfa1bhttps://pan.quark.cn/s/90ad8fba8347https://pan.quark.cn/s/d9d72152d3cf

二、核心特性(以AVL树为例)

  1. 继承BST特性:左子树所有节点值 < 根节点值,右子树所有节点值 > 根节点值,中序遍历为有序序列。
  2. 平衡约束:任意节点的左右子树高度差不超过1。
  3. 动态调整:插入/删除后若平衡被破坏,通过「旋转操作」恢复平衡。

三、平衡破坏与修复(旋转操作)

1. 失衡场景(4种核心情况)

失衡类型 触发条件(以失衡节点为根) 平衡因子特征
左左失衡(LL) 根节点左子树的左子树插入节点 根BF=2,左子树BF=1
右右失衡(RR) 根节点右子树的右子树插入节点 根BF=-2,右子树BF=-1
左右失衡(LR) 根节点左子树的右子树插入节点 根BF=2,左子树BF=-1
右左失衡(RL) 根节点右子树的左子树插入节点 根BF=-2,右子树BF=1

2. 旋转修复方法(可执行步骤)

(1)LL失衡:右旋转
复制代码
# 示例:根节点为A,左子树为B,B的左子树为C
1. 将B的右子树设为A的左子树(若B有右子树);
2. 将A设为B的右子树;
3. 更新A和B的高度(从下往上更新);
4. 将B设为新的根节点。
(2)RR失衡:左旋转
复制代码
# 示例:根节点为A,右子树为B,B的右子树为C
1. 将B的左子树设为A的右子树(若B有左子树);
2. 将A设为B的左子树;
3. 更新A和B的高度;
4. 将B设为新的根节点。
(3)LR失衡:先左旋转再右旋转
复制代码
1. 对根节点的左子树(B)执行左旋转,将LR转化为LL;
2. 对根节点(A)执行右旋转,恢复平衡。
(4)RL失衡:先右旋转再左旋转
复制代码
1. 对根节点的右子树(B)执行右旋转,将RL转化为RR;
2. 对根节点(A)执行左旋转,恢复平衡。

四、核心操作时间复杂度

操作 时间复杂度 说明
查找 O(log n) 树高为log₂n(严格平衡)
插入 O(log n) 最多旋转2次(插入仅影响一条路径)
删除 O(log n) 最多旋转O(log n)次(可能影响多条路径)
高度更新 O(1) 每个节点仅需记录左右子树高度

五、AVL树 vs 红黑树(面试高频对比)

对比维度 AVL树 红黑树
平衡程度 严格平衡( BF
旋转次数 插入最多2次,删除较多 插入最多2次,删除最多3次
空间开销 需存储高度信息(每个节点额外int) 需存储颜色(每个节点额外bit)
适用场景 查询操作远多于插入删除 插入删除频繁(如HashMap、TreeSet)
工业界应用 较少(Redis有序集合早期版本) 广泛(Java集合、Linux内核)

六、代码实现示例(Java:AVL树核心逻辑)

java 复制代码
class AVLNode {
    int val;
    int height; // 节点高度(默认1)
    AVLNode left, right;

    public AVLNode(int val) {
        this.val = val;
        this.height = 1;
    }
}

class AVLTree {
    private AVLNode root;

    // 获取节点高度
    private int getHeight(AVLNode node) {
        return node == null ? 0 : node.height;
    }

    // 计算平衡因子
    private int getBalance(AVLNode node) {
        return node == null ? 0 : getHeight(node.left) - getHeight(node.right);
    }

    // 更新节点高度
    private void updateHeight(AVLNode node) {
        node.height = 1 + Math.max(getHeight(node.left), getHeight(node.right));
    }

    // 右旋转
    private AVLNode rightRotate(AVLNode y) {
        AVLNode x = y.left;
        AVLNode T2 = x.right;

        // 执行旋转
        x.right = y;
        y.left = T2;

        // 更新高度
        updateHeight(y);
        updateHeight(x);

        return x;
    }

    // 左旋转
    private AVLNode leftRotate(AVLNode x) {
        AVLNode y = x.right;
        AVLNode T2 = y.left;

        // 执行旋转
        y.left = x;
        x.right = T2;

        // 更新高度
        updateHeight(x);
        updateHeight(y);

        return y;
    }

    // 插入节点(递归实现)
    public AVLNode insert(AVLNode node, int val) {
        // 1. 执行BST插入
        if (node == null) return new AVLNode(val);
        if (val < node.val) node.left = insert(node.left, val);
        else if (val > node.val) node.right = insert(node.right, val);
        else return node; // 不允许重复值

        // 2. 更新高度
        updateHeight(node);

        // 3. 计算平衡因子,判断是否失衡
        int balance = getBalance(node);

        // 4. 处理4种失衡情况
        // LL
        if (balance > 1 && val < node.left.val)
            return rightRotate(node);
        // RR
        if (balance < -1 && val > node.right.val)
            return leftRotate(node);
        // LR
        if (balance > 1 && val > node.left.val) {
            node.left = leftRotate(node.left);
            return rightRotate(node);
        }
        // RL
        if (balance < -1 && val < node.right.val) {
            node.right = rightRotate(node.right);
            return leftRotate(node);
        }

        return node; // 未失衡,返回原节点
    }

    // 中序遍历(验证有序性)
    public void inorder(AVLNode node) {
        if (node != null) {
            inorder(node.left);
            System.out.print(node.val + " ");
            inorder(node.right);
        }
    }
}
相关推荐
D_FW7 小时前
数据结构第六章:图
数据结构·算法
Ka1Yan12 小时前
[链表] - 代码随想录 707. 设计链表
数据结构·算法·链表
scx2013100412 小时前
20260112树状数组总结
数据结构·c++·算法·树状数组
宵时待雨12 小时前
数据结构(初阶)笔记归纳3:顺序表的应用
c语言·开发语言·数据结构·笔记·算法
无限进步_13 小时前
【C语言&数据结构】二叉树遍历:从前序构建到中序输出
c语言·开发语言·数据结构·c++·算法·github·visual studio
菜鸟233号14 小时前
力扣518 零钱兑换II java实现
java·数据结构·算法·leetcode·动态规划
鱼跃鹰飞15 小时前
面试题:解释一下什么是全字段排序和rowid排序
数据结构·数据库·mysql
Ka1Yan17 小时前
[链表] - 代码随想录 206. 反转链表
数据结构·链表
学嵌入式的小杨同学17 小时前
C 语言实战:动态规划求解最长公共子串(连续),附完整实现与优化
数据结构·c++·算法·unity·游戏引擎·代理模式
良木生香17 小时前
【C语言进阶】文件操作的相关详解(1):
c语言·数据结构·c++