一、平衡二叉树(AVL 树)
设计目的:保证二叉排序树的高度为 $ O(\log_2 n) $,使插入、删除、查找等操作的最坏时间复杂度稳定在 $ O(\log_2 n) $。普通二叉排序树在极端情况下可能退化为链表,导致操作时间复杂度上升至 $ O(n) $。
核心定义:
- 满足 BST 性质:即对于任意节点,左子树所有节点值小于根节点值,右子树所有节点值大于根节点值。
- 平衡条件:任一节点的左右子树高度差(称为平衡因子)的绝对值不超过 1。平衡因子 = 左子树深度 - 右子树深度,其值只能是 -1、0 或 1。
高度特性:
含有 $ n $ 个节点的 AVL 树,其最大高度约为 $ 1.44 \log_2 n $,远优于最坏情况下的 $ O(n) $,接近完全二叉树的理想高度 $ \log_2 n $。
为了维持平衡,在插入或删除节点后若出现不平衡(平衡因子变为 ±2),需通过旋转操作(单旋:LL、RR;双旋:LR、RL)进行调整,恢复平衡。
二、线索二叉树(Threaded Binary Tree)
设计目的:解决普通二叉树中无法高效获取某节点在中序遍历中的前驱和后继的问题。同时利用原本浪费的空指针域(n 个节点共有 $ n+1 $ 个空指针)来存储有用的线索信息,提高空间利用率和遍历效率。
实现方式:
- 将空的左指针改为指向该节点在中序遍历中的前驱节点。
- 将空的右指针改为指向该节点在中序遍历中的后继节点。
- 引入两个标志位:
ltag:0 表示左指针指向左孩子,1 表示左指针为线索,指向前驱。rtag:0 表示右指针指向右孩子,1 表示右指针为线索,指向后继。
节点结构示例(C语言风格):
struct BTnode {
int data;
struct BTnode *left, *right;
int ltag; // 0: child, 1: thread (predecessor)
int rtag; // 0: child, 1: thread (successor)
};
通过中序线索化后,可以不使用栈或递归实现高效的中序遍历,直接沿线索访问下一个节点。
AVL树的四种旋转操作用于在插入或删除节点导致二叉树失去平衡(即某个节点的平衡因子变为±2)时,恢复其平衡性。这些旋转操作根据失衡节点与其子树中“过高”部分的位置关系分为四类:LL、RR、LR、RL。
一、四种旋转类型及适用场景
| 类型 | 全称 | 触发条件(以失衡节点为 z) | 场景描述 |
|---|---|---|---|
| LL型 | Left-Left | z 的左子树比右子树高 2,且 z 的左孩子(y)的左子树更高(平衡因子为 +1 或 +2) | 失衡路径连续向左,形成“左偏左”结构 |
| RR型 | Right-Right | z 的右子树比左子树高 2,且 z 的右孩子(y)的右子树更高(平衡因子为 -1 或 -2) | 失衡路径连续向右,“右偏右”结构 |
| LR型 | Left-Right | z 的左子树过高,但 z 的左孩子的右子树更高 | 先左后右的“Z”字形结构 |
| RL型 | Right-Left | z 的右子树过高,但 z 的右孩子的左子树更高 | 先右后左的镜像“Z”字形 |
📌 核心思想:通过一次或两次旋转将“过高”的中间节点提升为新的根,使树重新满足 AVL 平衡条件。
二、旋转操作实现(C语言风格)
假设节点结构如下:
typedef struct AVLNode {
int data;
struct AVLNode *left;
struct AVLNode *right;
int height; // 存储当前节点高度,便于计算平衡因子
} AVLNode;
辅助函数:获取高度与平衡因子
int max(int a, int b) {
return (a > b) ? a : b;
}
int height(AVLNode *node) {
return node == NULL ? 0 : node->height;
}
int getBalance(AVLNode *node) {
return node == NULL ? 0 : height(node->left) - height(node->right);
}
1. 右单旋(LL旋转)
适用于 LL 型失衡。
AVLNode* rotateRight(AVLNode* y) {
AVLNode* x = y->left;
AVLNode* T2 = x->right;
// 执行右旋
x->right = y;
y->left = T2;
// 更新高度(先更新子节点,再更新父节点)
y->height = max(height(y->left), height(y->right)) + 1;
x->height = max(height(x->left), height(x->right)) + 1;
return x; // 新的根节点
}
2. 左单旋(RR旋转)
适用于 RR 型失衡。
AVLNode* rotateLeft(AVLNode* x) {
AVLNode* y = x->right;
AVLNode* T2 = y->left;
// 执行左旋
y->left = x;
x->right = T2;
// 更新高度
x->height = max(height(x->left), height(x->right)) + 1;
y->height = max(height(y->left), height(y->right)) + 1;
return y; // 新的根节点
}
3. 左右双旋(LR旋转)
先对左孩子左旋(RR),再对根右旋(LL)。
AVLNode* rotateLeftRight(AVLNode* z) {
z->left = rotateLeft(z->left); // 左孩子做一次左旋(RR)
return rotateRight(z); // 再整体右旋(LL)
}
4. 右左双旋(RL旋转)
先对右孩子右旋(LL),再对根左旋(RR)。
AVLNode* rotateRightLeft(AVLNode* z) {
z->right = rotateRight(z->right); // 右孩子做一次右旋(LL)
return rotateLeft(z); // 再整体左旋(RR)
}
三、插入后的平衡调整逻辑示例
AVLNode* insert(AVLNode* node, int data) {
// 1. 标准BST插入
if (node == NULL) {
AVLNode* newNode = (AVLNode*)malloc(sizeof(AVLNode));
newNode->data = data;
newNode->left = newNode->right = NULL;
newNode->height = 1;
return newNode;
}
if (data < node->data)
node->left = insert(node->left, data);
else if (data > node->data)
node->right = insert(node->right, data);
else
return node; // 不允许重复值
// 2. 更新当前节点高度
node->height = max(height(node->left), height(node->right)) + 1;
// 3. 获取平衡因子
int balance = getBalance(node);
// 4. 判断失衡类型并旋转调整
// LL型
if (balance > 1 && data < node->left->data)
return rotateRight(node);
// RR型
if (balance < -1 && data > node->right->data)
return rotateLeft(node);
// LR型
if (balance > 1 && data > node->left->data) {
node->left = rotateLeft(node->left);
return rotateRight(node);
}
// RL型
if (balance < -1 && data < node->right->data) {
node->right = rotateRight(node->right);
return rotateLeft(node);
}
return node; // 已平衡,返回原节点
}
✅ 总结记忆口诀:
- “同方向用单旋”:LL → 右旋,RR → 左旋
- “异方向用双旋”:LR → 先左旋左孩子,再右旋根;RL → 先右旋右孩子,再左旋根

535

被折叠的 条评论
为什么被折叠?



