平衡二叉树(AVL)的趣味故事:猴王选接班人的智慧

0 阅读3分钟

📖 故事背景

森林里的猴王要选接班人,他设计了一个特别的选拔规则:

  1. 每只猴子按实力排名(数字代表实力大小)
  2. 排名树必须满足:任意猴子的左子树和右子树高度差 ≤ 1
  3. 违反规则时,需要立即通过「旋转」调整

🌳 为什么需要平衡?
普通二叉搜索树可能退化成链表(比如按顺序插入1,2,3,4),查找效率从O(log n)降到O(n)。AVL树通过自平衡确保高效操作!


🧠 AVL核心原理

java

class AVLNode {
    int value;       // 猴子的实力值
    AVLNode left;    // 左子树(实力小的猴子)
    AVLNode right;   // 右子树(实力强的猴子)
    int height;      // 关键!记录子树高度

    AVLNode(int val) {
        this.value = val;
        this.height = 1;  // 新节点初始高度为1
    }
}

⚖️ 平衡因子(Balance Factor)

java

int getBalance(AVLNode node) {
    if (node == null) return 0;
    // 左子树高度 - 右子树高度
    return height(node.left) - height(node.right);
}

平衡规则|BF| ≤ 1
BF > 1 → 左子树太高
BF < -1 → 右子树太高


🔄 四大旋转绝招(图解+代码)

场景1:左左失衡(LL)

🐒 问题:新猴子插在左子树的左侧
🛠️ 解法右旋

java

AVLNode rightRotate(AVLNode y) {
    AVLNode x = y.left;
    AVLNode T2 = x.right;

    // 旋转核心操作
    x.right = y;
    y.left = T2;

    // 更新高度(先更新下层节点y)
    y.height = 1 + Math.max(height(y.left), height(y.right));
    x.height = 1 + Math.max(height(x.left), height(x.right));

    return x; // 返回新的根节点
}

upload.wikimedia.org/wikipedia/c…


场景2:右右失衡(RR)

🐒 问题:新猴子插在右子树的右侧
🛠️ 解法左旋

java

AVLNode leftRotate(AVLNode x) {
    AVLNode y = x.right;
    AVLNode T2 = y.left;

    // 旋转核心操作
    y.left = x;
    x.right = T2;

    // 更新高度
    x.height = 1 + Math.max(height(x.left), height(x.right));
    y.height = 1 + Math.max(height(y.left), height(y.right));

    return y; // 返回新的根节点
}

upload.wikimedia.org/wikipedia/c…


场景3:左右失衡(LR)

🐒 问题:新猴子插在左子树的右侧
🛠️ 解法先左旋再右旋

java

// 1. 对左子树左旋
node.left = leftRotate(node.left);
// 2. 对当前节点右旋
return rightRotate(node);

upload.wikimedia.org/wikipedia/c…


场景4:右左失衡(RL)

🐒 问题:新猴子插在右子树的左侧
🛠️ 解法先右旋再左旋

java

// 1. 对右子树右旋
node.right = rightRotate(node.right);
// 2. 对当前节点左旋
return leftRotate(node);

upload.wikimedia.org/wikipedia/c…


🚀 完整插入操作(Java实现)

java

public class AVLTree {
    AVLNode root;

    // 插入入口
    void insert(int value) {
        root = insertNode(root, value);
    }

    private AVLNode insertNode(AVLNode node, int value) {
        // 1. 标准BST插入
        if (node == null) return new AVLNode(value);
        if (value < node.value) 
            node.left = insertNode(node.left, value);
        else if (value > node.value) 
            node.right = insertNode(node.right, value);
        else 
            return node; // 值相等不插入

        // 2. 更新当前节点高度
        node.height = 1 + Math.max(height(node.left), height(node.right));

        // 3. 检查平衡因子
        int balance = getBalance(node);

        // 4. 处理四种失衡情况
        // 左左失衡
        if (balance > 1 && value < node.left.value) 
            return rightRotate(node);

        // 右右失衡
        if (balance < -1 && value > node.right.value) 
            return leftRotate(node);

        // 左右失衡
        if (balance > 1 && value > node.left.value) {
            node.left = leftRotate(node.left);
            return rightRotate(node);
        }

        // 右左失衡
        if (balance < -1 && value < node.right.value) {
            node.right = rightRotate(node.right);
            return leftRotate(node);
        }

        return node; // 平衡则直接返回
    }

    // 辅助方法:获取节点高度
    int height(AVLNode node) {
        return node == null ? 0 : node.height;
    }
}

💡 关键总结

特性说明
平衡定义任意节点左右子树高度差 ≤ 1
核心优势保证最坏情况下操作复杂度为O(log n)
旋转操作LL/RR(单旋), LR/RL(双旋)
时间复杂度插入/删除/查找均为O(log n)
应用场景数据库索引、实时系统等需要稳定性能的场景

🌟 猴王的智慧:通过动态平衡,AVL树确保所有猴子(节点)都能被快速找到,避免出现"一支独大"的不公平现象!

通过这个有趣的故事和清晰的代码示例,相信你已经掌握了AVL树的精髓!试着画一画不同旋转场景的树形图,理解会更深刻哦~