js实现自平衡树 AVL tree

本文介绍了JavaScript实现AVL树的过程,包括插入和删除节点的特殊情况。在插入节点后,通过检查节点的平衡因子来判断是否需要进行平衡操作,如单旋转(LL, RR)和双旋转(RL, LR)。在删除节点时,根据节点的平衡因子和子节点情况决定是否进行旋转以保持平衡。文章提供了相关的代码示例,详细解释了自平衡树的平衡调整策略。" 86384877,7610454,Python+unittest+requests接口自动化测试实践与问题解析,"['Python编程', '测试工具', '自动化测试', '接口测试', 'unittest框架']

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1. 笔记

节点高度:Math.max(左节点高度, 右节点高度)+1
平衡因子:左节点高度 - 右节点高度,节点内数字表示当前节点的平衡因子。
平衡操作共有四种情况。
1. 向右的单旋转 LL: 当前节点的左节点比右节点重,当前节点的左节点的左节点比右节点重。
在这里插入图片描述
2. 向左的单旋转 RR: 当前节点的右节点比左节点重,当前节点的右节点的右节点比左节点重。
3. 向左的双旋转 RL: 当前节点的右节点比左节点重,当前节点的右节点的左节点比右节点重。
先对 当前节点的右节点 进行一次向右的单旋转,在对当前节点进行向左的单旋转。
4. 向右的双旋转 LR: 当前节点的左节点比右节点重,当前节点的左节点的右节点比左节点重。
先对 当前节点的左节点 进行一次向左的单旋转,在对当前节点进行向右的单旋转。
在这里插入图片描述
基于二叉搜索树的insert方法,AVL与之不同的则是,在每次插入后,我们需要检测插入后根节点的平衡因子,是否为0 1 -1三个数的其中一个。不是的话,则需要对当前树进行调整。
表格中数字是节点的平衡因子

根节点根的左子节点根的右子节点操作描述
1---平衡
0---平衡
-1---平衡
21平衡向右的单旋转LL
20平衡向右的单旋转LL
2-1平衡向右的双旋转LR
-2平衡1向左的双旋转RL
-2平衡-1向左的单旋转RR
-2平衡0向左的单旋转RR

insertNode

insert(value) {
	super.insert(value)
	return this.checkIsBalance()
}

removeNode

remove(value) {
	super.remove(value)
	return this.checkIsBalance()
}

checkIsBalance

checkIsBalance() {
	this.root = this._changeToBalance(this.root)
	return this
}

插入节点的特殊情况

为下图的左边的树,添加一个节点,使其不平衡。但我们发现,此时平衡因子所属情况与之前所描述的不同。此时我们需要进行递归,先对根节点的左节点进行平衡操作LL,完成后在对根节点进行平衡操作。这是根节点的平衡因子等于2的情况。当然等于-2的情况与之原理相同。
在这里插入图片描述
连续插入左节点,在自平衡过程中,会报错,因为this.root.left.right为null。故在LLRR使用try catch语句,处理异常。右节点:this.root.right为null,高度为-1
在这里插入图片描述
在右侧插入节点(红色),导致原树不平衡。只需做一次向左的单旋转RR,即可平衡。插入左侧节点时,原理一致。
在这里插入图片描述

删除节点的特殊情况

我们在看另外一种情况,下图删除了一个节点。左树平衡,右树不平衡。但根节点(平衡因子)为2,左子节点为0,右子节点平衡的情况之前没有讨论过。我们可以直接对其进行一次向右的单旋转即可平衡。当然,根节点为2,左子节点平衡,右子节点为0的情况与之原理相同。
在这里插入图片描述
在这里插入图片描述
_changeToBalance

_changeToBalance(node) {
     switch(this.getBalanceFactory(node)){
        case 2:
            const factoryLeft = this.getBalanceFactory(node.left)
            if([0,1].indexOf(factoryLeft) !== -1 || factoryLeft > 2){
                console.info('2 0 1')
                node = this.rotationLL(node)
            }else if(factoryLeft === -1){
                console.info('2 -1')
                node = this.rotationLR(node)
            }else if(factoryLeft === 2){
                console.info('2 2')
                node.left = this._changeToBalance(node.left)
            }else if(factoryLeft === -2){
                console.info('2 -2')
                node.left = this._changeToBalance(node.left)
            }
            return node
        case -2:
            const factoryRight = this.getBalanceFactory(node.right)
            if([0,1].indexOf(factoryRight) !== -1){
                console.info('-2 0 1')
                node = this.rotationRL(node)
            }else if(factoryRight === -1 || factoryRight < -2){
                console.info('-2 -1')
                node = this.rotationRR(node)
            }else if(factoryRight === 2){
                console.info('-2 2')
                node.right = this._changeToBalance(node.right)
            }else if(factoryRight === -2){
                console.info('-2 -2')
                node.right = this._changeToBalance(node.right)
                console.info(node.right)
            }
            return node
    }
	return node
}

2. 所有代码

2.1 自平衡树

class AVL extends BinarySearchTree {
    constructor(superConstructor) {
        super(superConstructor)
    }
    _getNodeHeight(node) {
        if(node === null){
            return -1
        }
        return Math.max(this._getNodeHeight(node.left), this._getNodeHeight(node.right))+1
    }
    getNodeHeight(node=this.root) {
        return this._getNodeHeight(node)
    }
    getBalanceFactory(node) {
        const factory = this.getNodeHeight(node.left) - this.getNodeHeight(node.right)
        return factory
    }
    // 向右单旋转
    rotationLL(node) {
        const tar = node.left
        try{
            node.left = tar.right
        }catch{
            node.left = null
        }
        tar.right = node
        return tar
    }
    // 向左单旋转
    rotationRR(node) {
        const tar = node.right
        try{
            node.right = tar.left
        }catch{
            node.right = null
        }
        tar.left = node
        return tar
    }
    // 向右双旋转
    rotationRL(node) {
        node.right = this.rotationLL(node.right)
        return this.rotationRR(node)
    }
    // 向左双旋转
    rotationLR(node) {
        node.left = this.rotationRR(node.left)
        return this.rotationLL(node)
    }
    _changeToBalance(node) {
        switch(this.getBalanceFactory(node)){
	        case 2:
	            const factoryLeft = this.getBalanceFactory(node.left)
	            if([0,1].indexOf(factoryLeft) !== -1 || factoryLeft > 2){
	                console.info('2 0 1')
	                node = this.rotationLL(node)
	            }else if(factoryLeft === -1){
	                console.info('2 -1')
	                node = this.rotationLR(node)
	            }else if(factoryLeft === 2){
	                console.info('2 2')
	                node.left = this._changeToBalance(node.left)
	            }else if(factoryLeft === -2){
	                console.info('2 -2')
	                node.left = this._changeToBalance(node.left)
	            }
	            return node
	        case -2:
	            const factoryRight = this.getBalanceFactory(node.right)
	            if([0,1].indexOf(factoryRight) !== -1){
	                console.info('-2 0 1')
	                node = this.rotationRL(node)
	            }else if(factoryRight === -1 || factoryRight < -2){
	                console.info('-2 -1')
	                node = this.rotationRR(node)
	            }else if(factoryRight === 2){
	                console.info('-2 2')
	                node.right = this._changeToBalance(node.right)
	            }else if(factoryRight === -2){
	                console.info('-2 -2')
	                node.right = this._changeToBalance(node.right)
	                console.info(node.right)
	            }
	            return node
	    }
        return node
    }
    checkIsBalance() {
        this.root = this._changeToBalance(this.root)
        return this
    }
    insert(value) {
        super.insert(value)
        return this.checkIsBalance()
    }
    remove(value) {
        super.remove(value)
        return this.checkIsBalance()
    }
}

2.2 二叉搜索树

class Node {
	constructor(value) {
		this.value = value
		this.left = null
		this.right = null
	}
}
class BinarySearchTree {
    constructor(value) {
        this.root = new Node(value)
    }
    _insertNode(node, value) {
        if(node.value > value){
            if(node.left === null){
                node.left = new Node(value)
            }else{
                this._insertNode(node.left, value)
            }
        }else{
            if(node.right === null){
                node.right = new Node(value)
            }else{
                this._insertNode(node.right, value)
            }
        }
    }
    insert(value) {
        if(this.root === null) {
            this.root = new Node(value)
        }else{
            this._insertNode(this.root, value)
        }
        return this
    }
    // 中序遍历: 左 -> 中 -> 右
    _inOrderTra(node, res) {
        if(node === null) { return }
        this._inOrderTra(node.left, res)
        res.push(node.value)
        this._inOrderTra(node.right, res)
    }
    inOrderTra() {
        let res = []
        this._inOrderTra(this.root, res)
        return res
    }
    // 前序遍历: 中 -> 左 -> 右
    _preOrderTra(node, res) {
        if(node === null) { return }
        res.push(node.value)
        this._preOrderTra(node.left, res)
        this._preOrderTra(node.right, res)
    }
    preOrderTra() {
        let res = []
        this._preOrderTra(this.root, res)
        return res
    }
    // 后序遍历: 左 -> 右 -> 中
    _postOrderTra(node, res) {
        if(node === null) { return }
        this._postOrderTra(node.left, res)
        this._postOrderTra(node.right, res)
        res.push(node.value)
    }
    postOrderTra() {
        let res = []
        this._postOrderTra(this.root, res)
        return res
    }
    _getMin(node) { 
        if(node.left === null){ return node }
        return this._getMin(node.left) 
    }
    getMin() {
        return this._getMin(this.root)
    }
    _getMax(node) { 
        if(node.right === null){ return node }
        return this._getMax(node.right) 
    }
    getMax() {
        return this._getMax(this.root)
    }
    _search(node, value) {
        if(node.value > value) {
            if(node.left === null) {
                throw 'Function Search: ' + value + ' 未找到'
            }else if(node.left.value === value) {
                return node.left
            }else {
                return this._search(node.left, value)
            }
        }else {
            if(node.right === null) {
                throw 'Function Search: ' + value + ' 未找到'
            }else if(node.right.value === value) {
                return node.right
            }else {
                return this._search(node.right, value)
            }
        }
    }
    search(value) {
        if(value === this.root.value){
            return this.root
        }
        return this._search(this.root, value)
    }
    _removeNode(target, node=this.root) {
        // 寻找删除节点的父节点
        if(node.left !== target && node.right !== target){
            if(node.value > target.value){
                return this._removeNode(target, node.left)
            }else{
                return this._removeNode(target, node.right)
            }
        }
        if(target.left === null && target.right === null){
            // 删除的节点无子节点
            return node.left === target ? node.left = null : node.right = null
        }else if(target.left === null || target.right === null){
            // 删除的节点只包含一个子节点
            const son = target.left === null ? target.right : target.left
            return node.left === target ? node.left = son : node.right = son
        }else if(target.left !== null && target.right !== null){
            // 删除的节点包含两个子节点
            const displace = this._getMin(target.right)
            return node.left === target ? node.left = displace : node.right = displace
        }
    }
    remove(value) {
        const target = this.search(value)
        if(target === this.root){
            throw 'Function remove: 不能删除根节点'
        }
        this._removeNode(target)
        return this
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值