文章目录
概念
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),需要恢复树的平衡。
- 保存不平衡树node的左子树temp
- node左子树替换为temp的右子树
- 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
- 先对右子树进行LL右旋,整个树形成RR条件
- 再对整个树RR左旋
rotationRL(node) {
node.right = this.rotationLL(node.right);
return this.rotationRR(node);
}
3 插入结点
插入方法和bst是一样的,不过还需要讨论不平衡的情况
- 如果在向左侧子树插入节点后树不平衡了,我们需要比较是否插入的键小于左侧子节点的键。如果是,我们要进行LL 旋转。否则,要进行LR 旋转。
- 如果在向右侧子树插入节点后树不平衡了,我们需要比较是否插入的键小于右侧子节点的键。如果是,我们要进行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);