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