平衡二叉树的介绍与实现

168 阅读3分钟

平衡二叉树(通常指 AVL 树)是一种自平衡的二叉查找树,它的特点是:对于树中的每一个节点,其左子树和右子树的高度差的绝对值不超过1。即,任意节点的平衡因子(左子树高度 - 右子树高度)的绝对值最大为1。

为了实现一个 AVL 树,我们需要执行以下步骤:

  1. 插入节点:在插入新节点时,首先按二叉搜索树的规则插入节点,然后调整树以保持平衡。
  2. 旋转操作:当树不平衡时,需要执行左旋和右旋操作来恢复平衡。
  3. 平衡因子:通过计算节点的左子树和右子树的高度差来确定树是否平衡。如果某个节点的平衡因子大于 1 或小于 -1,说明该节点不平衡,必须执行旋转操作来恢复平衡。

1. 平衡因子和旋转

  • 平衡因子:节点的左子树高度减去右子树高度。

    • 平衡因子 = 左子树高度 - 右子树高度
    • 如果平衡因子 > 1,说明左子树比右子树高,需要进行右旋。
    • 如果平衡因子 < -1,说明右子树比左子树高,需要进行左旋。
  • 旋转操作

    • 右旋(LL)(右单旋):适用于左子树过高的情况,通过将左子树的根节点上升为新根。

54ccfc6ab35893cafb200885920bac00.png

  • 左旋(RR)(左单旋):适用于右子树过高的情况, 通过将右子树的根节点上升为新根。

860fb535e2d898e78b73548a068ecce8.png

  • 左右旋(LR):当左子树的右子树比左子树还高时,先对左子树进行左旋,再对根节点进行右旋。
  • 右左旋(RL):当右子树的左子树比右子树还高时,先对右子树进行右旋,再对根节点进行左旋。

2. JavaScript 实现 AVL 树

下面是一个基于 JavaScript 实现的简单 AVL 树:

class TreeNode {
    constructor(value) {
        this.value = value; // 节点的值
        this.left = null; // 左子节点
        this.right = null; // 右子节点
        this.height = 1; // 节点的高度,默认值为 1
    }
}

class AVLTree {
    constructor() {
        this.root = null;
    }

    // 获取节点的高度
    getHeight(node) {
        return node ? node.height : 0;
    }

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

    // 获取平衡因子
    getBalanceFactor(node) {
        return node ? this.getHeight(node.left) - this.getHeight(node.right) : 0;
    }

    // 右旋操作(针对左子树过高的情况) 
    /*
    当前节点的左节点作为新的根节点,把当前节点作为新的根节点的右节点,同时把新根节点的原右节点作为当前节点的左节点
    */
    rightRotate(current) {
        const newRoot = current.left;
        const T2 = newRoot.right;

        // 进行旋转
        newRoot.right = current;
        current.left = T2;

        // 更新高度
        this.updateHeight(current);
        this.updateHeight(newRoot);

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

    // 左旋操作(针对右子树过高的情况)
    /*
    当前节点的右节点作为新的根节点,把当前节点作为新的根节点的左节点,同时把新节点的原左节点作为当前节点的右节点
    */
    leftRotate(current) {
        const newRoot = current.right;
        const T2 = newRoot.left;

        // 进行旋转
        newRoot.left = current;
        current.right = T2;

        // 更新高度
        this.updateHeight(current);
        this.updateHeight(newRoot);

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

    // 插入节点
    insert(value) {
        this.root = this._insert(this.root, value);
    }

    // 递归插入节点
    _insert(node, value) {
        if (!node) {
            return new TreeNode(value);
        }

        // 插入到左子树或右子树
        if (value < node.value) {
            node.left = this._insert(node.left, value);
        } else if (value > node.value) {
            node.right = this._insert(node.right, value);
        } else {
            return node; // 不插入重复的元素
        }

        // 更新当前节点的高度
        this.updateHeight(node);

        // 检查当前节点的平衡因子
        const balance = this.getBalanceFactor(node);

        // 进行旋转操作来恢复平衡
        // 左左情况(左子树的左子树高)
        if (balance > 1 && value < node.left.value) {
            return this.rightRotate(node);
        }

        // 右右情况(右子树的右子树高)
        if (balance < -1 && value > node.right.value) {
            return this.leftRotate(node);
        }

        // 左右情况(左子树的右子树高)
        if (balance > 1 && value > node.left.value) {
            node.left = this.leftRotate(node.left);
            return this.rightRotate(node);
        }

        // 右左情况(右子树的左子树高)
        if (balance < -1 && value < node.right.value) {
            node.right = this.rightRotate(node.right);
            return this.leftRotate(node);
        }

        return node;
    }

    // 中序遍历(按升序输出)
    inOrderTraversal(node = this.root) {
        if (node) {
            this.inOrderTraversal(node.left);
            console.log(node.value);
            this.inOrderTraversal(node.right);
        }
    }
}

// 使用示例
const avlTree = new AVLTree();
avlTree.insert(10);
avlTree.insert(20);
avlTree.insert(30);
avlTree.insert(15);
avlTree.insert(25);

console.log('中序遍历:');
avlTree.inOrderTraversal(); // 输出: 10, 15, 20, 25, 30 代码示例中只进行了一次左旋(RR)