JS数据结构5:树-自平衡二叉树

概念

BST 存在一个问题:
取决于你添加的节点数,树的一条边可能会非常深;
也就是说,树的一条分支会有很多层,而其他的分支却只有几层,
这会造成很大的性能问题
所以需要平衡二叉搜索树来解决这个问题
在这里插入图片描述
平衡二叉树的概念

  • 在AVL 树中,需要对每个节点计算右子树高度(hr)和左子树高度(hl)之间的差值,该值(hr-hl)应为0、1 或 -1
  • 如果结果不是这三个值之一,则需要平衡该AVL 树。
    这就是平衡因子的概念。
    在这里插入图片描述

AVL的实现

  • 我们可以扩展我们写的BST 类,只需要覆盖用来维持AVL 树平衡的方法,也就是insert、insertNode 和removeNode 方法。
  • 所有其他的BST 方法将会被AVLTree 类继承。

1 平衡因子的计算

const BalanceFactor = {
    UNBALANCED_RIGHT: 1,
    SLIGHTLY_UNBALANCED_RIGHT: 2,
    BALANCED: 3,
    SLIGHTLY_UNBALANCED_LEFT: 4,
    UNBALANCED_LEFT: 5
};
// 树的高度
getNodeHeight(node) {
    if (!node) {
        return -1;
    }
    return Math.max(this.getNodeHeight(node.left), this.getNodeHeight(node.right)) + 1;
}

// 两个树的高度差
getBalanceFactory(node) {
    let heightDifference = this.getNodeHeight(node.left) - this.getNodeHeight(node.right);
    switch (heightDifference) {
        case -2:    // 右边不平衡
            return BalanceFactory.UNBALANCE_RIGHT;
        case -1:    // 右边有点点不平衡
            return BalanceFactory.SLIGHTLY_UNBALANCE_RIGHT;
        case 0:    // 平衡
            return BalanceFactory.BALANCE;
        case 1:    // 左边有点点不平衡
            return BalanceFactory.SLIGHTLY_UNBALANCE_LEFT;
        case 2:    // 左边不平衡
            return BalanceFactory.UNBALANCE_LEFT;
    }
}

2 平衡操作

  • 左-左(LL):向右的单旋转
  • 右-右(RR):向左的单旋转
  • 左-右(LR):向右的双旋转(先LL 旋转,再RR 旋转)
  • 右-左(RL):向左的双旋转(先RR 旋转,再LL 旋转)

向右的单旋转LL

这种情况出现于节点的左侧子节点的高度大于右侧子节点的高度时,并且左侧子节点也是平衡或左侧较重的,这个时候需要左子树放在上面。者就是所谓的右单旋转
在这里插入图片描述
假设向AVL 树插入节点5,这会造成树失衡(节点50-Y 高度为+2),需要恢复树的平衡。

  1. 保存不平衡树node的左子树temp
  2. node左子树替换为temp的右子树
  3. temp的右子树替换为改变后的node树

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

    rotationLL(node) {
        let temp = node.left;
        node.left = temp.right;
        temp.right = node;
        return temp;
    }

在这里插入图片描述

RR:向左的单旋转

与LL情况相反。它出现于右侧子节点的高度大于左侧子节点的高度,并且右侧子节点也是平衡或右侧较重的

    rotationRR(node) {
        let temp = node.right;
        node.right = temp.left;
        temp.left = node;
        return temp;
    }

向右的双旋转LR:Left-Right

  • 这种情况出现于左侧子节点的高度大于右侧子节点的高度,并且左侧子节点右侧较重.
  • 简而言之,左子树重了,左子树中的右子树也重了
  • 在这种情况下,可以先对左侧子节点进行RR左旋转来修复,这样会形成LL的情况,然后再对不平衡的节点进行一个LL右旋转来修复

在这里插入图片描述

    rotationLR(node) {
        node.left = this.rotationRR(node.left);
        return this.rotationLL(node);
    }

向左的双旋转RL: Right-Left

  1. 先对右子树进行LL右旋,整个树形成RR条件
  2. 再对整个树RR左旋
    在这里插入图片描述
    rotationRL(node) {
        node.right = this.rotationLL(node.right);
        return this.rotationRR(node);
    }

3 插入结点

插入方法和bst是一样的,不过还需要讨论不平衡的情况

  1. 如果在向左侧子树插入节点后树不平衡了,我们需要比较是否插入的键小于左侧子节点的键。如果是,我们要进行LL 旋转。否则,要进行LR 旋转。
  2. 如果在向右侧子树插入节点后树不平衡了,我们需要比较是否插入的键小于右侧子节点的键。如果是,我们要进行RR 旋转。否则,要进行RL 旋转。
insert(key) {
    this.root = this.insertNode(this.root, key);
}
insertNode(node, key) {
    // 像在BST 树中一样插入节点
    if (node == null) {
        return new Node(key);
    } else if (this.compareFn(key, node.key) === Compare.LESS_THAN) {
        node.left = this.insertNode(node.left, key);
    } else if (this.compareFn(key, node.key) === Compare.BIGGER_THAN) {
        node.right = this.insertNode(node.right, key);
    } else {
        return node; // 重复的键
    }
    // 如果需要,将树进行平衡操作
    const balanceFactor = this.getBalanceFactor(node);
   	// 左不平衡
    if (balanceFactor === BalanceFactor.UNBALANCED_LEFT) {
    	// 判断是否存在LR情况
        if (this.compareFn(key, node.left.key) === Compare.LESS_THAN) {
            node = this.rotationLL(node);
        } else {
            return this.rotationLR(node);
        }
    }
    // 右不平衡
    if (balanceFactor === BalanceFactor.UNBALANCED_RIGHT) {
        if (this.compareFn(key, node.right.key) === Compare.BIGGER_THAN) {
            node = this.rotationRR(node);
        } else {
            return this.rotationRL(node);
        }
    }
    return node;
}

4 删除结点

removeNode(node, key) {
    node = super.removeNode(node, key);
    if (node == null) {
        return node; // null,不需要进行平衡
    }
    // 检测树是否平衡
    const balanceFactor = this.getBalanceFactor(node);
    // 左不平衡 
    if (balanceFactor === BalanceFactor.UNBALANCED_LEFT) {
        // 左子树状态
        const balanceFactorLeft = this.getBalanceFactor(node.left);
        // 如果是平衡(h = 0)或者有点不平衡(h = 1),LL
        if (balanceFactorLeft === BalanceFactor.BALANCED ||
            balanceFactorLeft === BalanceFactor.SLIGHTLY_UNBALANCED_LEFT) {
            return this.rotationLL(node);
        }
        // 如果不平衡(h = 2)LR
        if (balanceFactorLeft === BalanceFactor.SLIGHTLY_UNBALANCED_RIGHT) {
            return this.rotationLR(node.left);
        }
    }
    // 右不平衡
    if (balanceFactor === BalanceFactor.UNBALANCED_RIGHT) {
        // 右子树
        const balanceFactorRight = this.getBalanceFactor(node.right);
        if (balanceFactorRight === BalanceFactor.BALANCED ||
            balanceFactorRight === BalanceFactor.SLIGHTLY_UNBALANCED_RIGHT) {
            return this.rotationRR(node);
        }
        if (balanceFactorRight === BalanceFactor.SLIGHTLY_UNBALANCED_LEFT) {
            return this.rotationRL(node.right);
        }
    }
    return node;
}

完整代码

class Node {
    constructor(key) {
        this.key = key; //  节点值
        this.left = null; // 左侧子节点引用
        this.right = null; // 右侧子节点引用
    }
}

const Compare = {
    LESS_THAN: -1,
    BIGGER_THAN: 1
};

class BinarySearchTree {
    constructor() {
        this.root = null; //  Node 类型的根节点 默认就是null
    }

    compareFn(a, b) {
        if (a === b) { //
            return 0;
        }
        return a < b ? Compare.LESS_THAN : Compare.BIGGER_THAN; // 比较函数, a<b返回-1. 大于则返回正1
    }

    insert(key) {
        if (this.root == null) {
            this.root = new Node(key);
        } else {
            this.insertNode(this.root, key);
        }
    }

    insertNode(node, key) {
        if (this.compareFn(key, node.key) === Compare.LESS_THAN) {
            if (node.left == null) {
                node.left = new Node(key);
            } else {
                this.insertNode(node.left, key);
            }
        } else {
            if (node.right == null) {
                node.right = new Node(key);
            } else {
                this.insertNode(node.right, key);
            }
        }
    }

    inOrderTraverse(callback) {
        this.inOrderTraverseNode(this.root, callback);
    }

    inOrderTraverseNode(node, callback) {
        if (node != null) {
            this.inOrderTraverseNode(node.left, callback);
            callback(node.key);
            this.inOrderTraverseNode(node.right, callback);
        }
    }

    preOrderTraverse(callback) {
        this.preOrderTraverseNode(this.root, callback);
    }

    preOrderTraverseNode(node, callback) {
        if (node != null) {
            callback(node.key);
            this.preOrderTraverseNode(node.left, callback);
            this.preOrderTraverseNode(node.right, callback);
        }
    }

    postOrderTraverse(callback) {
        this.postOrderTraverseNode(this.root, callback);
    }

    postOrderTraverseNode(node, callback) {
        if (node != null) {
            this.postOrderTraverseNode(node.left, callback);
            this.postOrderTraverseNode(node.right, callback);
            callback(node.key);
        }
    }

    min() {
        return this.minNode(this.root);
    }

    minNode(node) {
        let current = node;
        while (current != null && current.left != null) {
            current = current.left;
        }
        return current;
    }

    max() {
        return this.maxNode(this.root);
    }

    maxNode(node) {
        let current = node;
        while (current != null && current.right != null) {
            current = current.right;
        }
        return current;
    }

    search(key) {
        return this.searchNode(this.root, key);
    }

    searchNode(node, key) {
        if (node == null) {
            return false;
        }
        if (this.compareFn(key, node.key) === Compare.LESS_THAN) {
            return this.searchNode(node.left, key);
        } else if (
            this.compareFn(key, node.key) === Compare.BIGGER_THAN
        ) {
            return this.searchNode(node.right, key);
        } else {
            return true;
        }
    }

    remove(key) {
        this.root = this.removeNode(this.root, key);
    }

    removeNode(node, key) {
        if (node == null) {
            return null;
        }
        if (this.compareFn(key, node.key) === Compare.LESS_THAN) {
            node.left = this.removeNode(node.left, key);
            return node;
        } else if (
            this.compareFn(key, node.key) === Compare.BIGGER_THAN
        ) {
            node.right = this.removeNode(node.right, key);
            return node;
        } else {
            // 键等于node.key
            // 第一种情况
            if (node.left == null && node.right == null) {
                node = null;
                return node;
            }
            // 第二种情况
            if (node.left == null) {
                node = node.right;
                return node;
            } else if (node.right == null) {
                node = node.left;
                return node;
            }
            // 第三种情况
            const aux = this.minNode(node.right);
            node.key = aux.key;
            node.right = this.removeNode(node.right, aux.key);
            return node;
        }
    }
}

const BalanceFactor = {
    UNBALANCED_RIGHT: 1,
    SLIGHTLY_UNBALANCED_RIGHT: 2,
    BALANCED: 3,
    SLIGHTLY_UNBALANCED_LEFT: 4,
    UNBALANCED_LEFT: 5
};



class AVLTree extends BinarySearchTree {
    constructor() {
        super();
        this.root = null;
    }
    getNodeHeight(node) {
        if (node == null) {
            return -1;
        }
        return Math.max(
            this.getNodeHeight(node.left), this.getNodeHeight(node.right)
        ) + 1;
    }
    getBalanceFactor(node) {
        const heightDifference = this.getNodeHeight(node.left) -
            this.getNodeHeight(node.right);
        switch (heightDifference) {
            case -2:
                return BalanceFactor.UNBALANCED_RIGHT;
            case -1:
                return BalanceFactor.SLIGHTLY_UNBALANCED_RIGHT;
            case 1:
                return BalanceFactor.SLIGHTLY_UNBALANCED_LEFT;
            case 2:
                return BalanceFactor.UNBALANCED_LEFT;
            default:
                return BalanceFactor.BALANCED;
        }
    }
    rotationLL(node) {
        const tmp = node.left;
        node.left = tmp.right;
        tmp.right = node;
        return tmp;
    }
    rotationRR(node) {
        const tmp = node.right;
        node.right = tmp.left;
        tmp.left = node;
        return tmp;
    }
    rotationLR(node) {
        node.left = this.rotationRR(node.left);
        return this.rotationLL(node);
    }
    rotationRL(node) {
        node.right = this.rotationLL(node.right);
        return this.rotationRR(node);
    }
    insert(key) {
        this.root = this.insertNode(this.root, key);
    }
    insertNode(node, key) {
        // 像在BST 树中一样插入节点
        if (node == null) {
            return new Node(key);
        } else if (this.compareFn(key, node.key) === Compare.LESS_THAN) {
            node.left = this.insertNode(node.left, key);
        } else if (this.compareFn(key, node.key) === Compare.BIGGER_THAN) {
            node.right = this.insertNode(node.right, key);
        } else {
            return node; // 重复的键
        }
        // 如果需要,将树进行平衡操作
        const balanceFactor = this.getBalanceFactor(node);
        if (balanceFactor === BalanceFactor.UNBALANCED_LEFT) {
            if (this.compareFn(key, node.left.key) === Compare.LESS_THAN) {
                node = this.rotationLL(node);
            } else {
                return this.rotationLR(node);
            }
        }
        if (balanceFactor === BalanceFactor.UNBALANCED_RIGHT) {
            if (
                this.compareFn(key, node.right.key) === Compare.BIGGER_THAN
            ) {
                node = this.rotationRR(node);
            } else {
                return this.rotationRL(node);
            }
        }
        return node;
    }
    removeNode(node, key) {
        node = super.removeNode(node, key);
        if (node == null) {
            return node; // null,不需要进行平衡
        }
        // 检测树是否平衡
        const balanceFactor = this.getBalanceFactor(node);
        // 左不平衡 
        if (balanceFactor === BalanceFactor.UNBALANCED_LEFT) {
            // 左子树状态
            const balanceFactorLeft = this.getBalanceFactor(node.left);
            // 如果是平衡(h = 0)或者有点不平衡(h = 1),LL
            if (balanceFactorLeft === BalanceFactor.BALANCED ||
                balanceFactorLeft === BalanceFactor.SLIGHTLY_UNBALANCED_LEFT) {
                return this.rotationLL(node);
            }
            // 如果不平衡(h = 2)LR
            if (balanceFactorLeft === BalanceFactor.SLIGHTLY_UNBALANCED_RIGHT) {
                return this.rotationLR(node.left);
            }
        }
        // 右不平衡
        if (balanceFactor === BalanceFactor.UNBALANCED_RIGHT) {
            // 右子树
            const balanceFactorRight = this.getBalanceFactor(node.right);
            if (balanceFactorRight === BalanceFactor.BALANCED ||
                balanceFactorRight === BalanceFactor.SLIGHTLY_UNBALANCED_RIGHT) {
                return this.rotationRR(node);
            }
            if (balanceFactorRight === BalanceFactor.SLIGHTLY_UNBALANCED_LEFT) {
                return this.rotationRL(node.right);
            }
        }
        return node;
    }
}

let avl = new AVLTree();
avl.insert(70);
avl.insert(50);
avl.insert(80);
avl.insert(72);
avl.insert(90);
avl.insert(75);
avl.remove(70);
avl.remove(50);
console.log(avl);
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值