平衡二叉树(通常指 AVL 树)是一种自平衡的二叉查找树,它的特点是:对于树中的每一个节点,其左子树和右子树的高度差的绝对值不超过1。即,任意节点的平衡因子(左子树高度 - 右子树高度)的绝对值最大为1。
为了实现一个 AVL 树,我们需要执行以下步骤:
- 插入节点:在插入新节点时,首先按二叉搜索树的规则插入节点,然后调整树以保持平衡。
- 旋转操作:当树不平衡时,需要执行左旋和右旋操作来恢复平衡。
- 平衡因子:通过计算节点的左子树和右子树的高度差来确定树是否平衡。如果某个节点的平衡因子大于 1 或小于 -1,说明该节点不平衡,必须执行旋转操作来恢复平衡。
1. 平衡因子和旋转
-
平衡因子:节点的左子树高度减去右子树高度。
- 平衡因子 = 左子树高度 - 右子树高度
- 如果平衡因子 > 1,说明左子树比右子树高,需要进行右旋。
- 如果平衡因子 < -1,说明右子树比左子树高,需要进行左旋。
-
旋转操作:
- 右旋(LL)(右单旋):适用于左子树过高的情况,通过将左子树的根节点上升为新根。
- 左旋(RR)(左单旋):适用于右子树过高的情况, 通过将右子树的根节点上升为新根。
- 左右旋(LR):当左子树的右子树比左子树还高时,先对左子树进行左旋,再对根节点进行右旋。
- 右左旋(RL):当右子树的左子树比右子树还高时,先对右子树进行右旋,再对根节点进行左旋。
2. JavaScript 实现 AVL 树
下面是一个基于 JavaScript 实现的简单 AVL 树:
class TreeNode {
constructor(value) {
this.value = value; // 节点的值
this.left = null; // 左子节点
this.right = null; // 右子节点
this.height = 1; // 节点的高度,默认值为 1
}
}
class AVLTree {
constructor() {
this.root = null;
}
// 获取节点的高度
getHeight(node) {
return node ? node.height : 0;
}
// 更新节点的高度
updateHeight(node) {
node.height = Math.max(this.getHeight(node.left), this.getHeight(node.right)) + 1;
}
// 获取平衡因子
getBalanceFactor(node) {
return node ? this.getHeight(node.left) - this.getHeight(node.right) : 0;
}
// 右旋操作(针对左子树过高的情况)
/*
当前节点的左节点作为新的根节点,把当前节点作为新的根节点的右节点,同时把新根节点的原右节点作为当前节点的左节点
*/
rightRotate(current) {
const newRoot = current.left;
const T2 = newRoot.right;
// 进行旋转
newRoot.right = current;
current.left = T2;
// 更新高度
this.updateHeight(current);
this.updateHeight(newRoot);
// 返回新的根节点
return newRoot;
}
// 左旋操作(针对右子树过高的情况)
/*
当前节点的右节点作为新的根节点,把当前节点作为新的根节点的左节点,同时把新节点的原左节点作为当前节点的右节点
*/
leftRotate(current) {
const newRoot = current.right;
const T2 = newRoot.left;
// 进行旋转
newRoot.left = current;
current.right = T2;
// 更新高度
this.updateHeight(current);
this.updateHeight(newRoot);
// 返回新的根节点
return newRoot;
}
// 插入节点
insert(value) {
this.root = this._insert(this.root, value);
}
// 递归插入节点
_insert(node, value) {
if (!node) {
return new TreeNode(value);
}
// 插入到左子树或右子树
if (value < node.value) {
node.left = this._insert(node.left, value);
} else if (value > node.value) {
node.right = this._insert(node.right, value);
} else {
return node; // 不插入重复的元素
}
// 更新当前节点的高度
this.updateHeight(node);
// 检查当前节点的平衡因子
const balance = this.getBalanceFactor(node);
// 进行旋转操作来恢复平衡
// 左左情况(左子树的左子树高)
if (balance > 1 && value < node.left.value) {
return this.rightRotate(node);
}
// 右右情况(右子树的右子树高)
if (balance < -1 && value > node.right.value) {
return this.leftRotate(node);
}
// 左右情况(左子树的右子树高)
if (balance > 1 && value > node.left.value) {
node.left = this.leftRotate(node.left);
return this.rightRotate(node);
}
// 右左情况(右子树的左子树高)
if (balance < -1 && value < node.right.value) {
node.right = this.rightRotate(node.right);
return this.leftRotate(node);
}
return node;
}
// 中序遍历(按升序输出)
inOrderTraversal(node = this.root) {
if (node) {
this.inOrderTraversal(node.left);
console.log(node.value);
this.inOrderTraversal(node.right);
}
}
}
// 使用示例
const avlTree = new AVLTree();
avlTree.insert(10);
avlTree.insert(20);
avlTree.insert(30);
avlTree.insert(15);
avlTree.insert(25);
console.log('中序遍历:');
avlTree.inOrderTraversal(); // 输出: 10, 15, 20, 25, 30 代码示例中只进行了一次左旋(RR)