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('-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
}
}