S29 平衡二叉搜索树(AVL)

AVL 树是最早的自平衡二叉搜索树 (1962 年由 Adelson-Velsky 和 Landis 提出),核心特性是 "左右子树高度差(平衡因子)不超过 1",通过旋转操作 在插入 / 删除后维持平衡,确保所有操作的时间复杂度稳定在 O(logn)(树高严格控制为 O(logn))。

核心定义与特性

1. 平衡二叉搜索树的定义

一棵二叉搜索树(左小右大)满足以下条件则为 AVL 树:

  • 左、右子树均为 AVL 树;

  • 左右子树的高度差(平衡因子)的绝对值 ≤ 1。

2. 关键概念
  • 平衡因子(BF):节点的左子树高度 - 右子树高度(取值范围:-1、0、1);

  • 树高:空树高度为 - 1(或 0,需统一定义),叶子节点高度为 0(或 1),非叶子节点高度 = 1 + max (左子树高度,右子树高度);

  • 失衡:节点的平衡因子绝对值 > 1(即 BF=2 或 BF=-2),需通过旋转修复

  • 平衡因子的计算在AVL树中,每个节点的平衡因子只能是-1、0或1:

  • -1:表示左子树比右子树高。

  • 0:表示左子树和右子树等高。

  • 1:表示右子树比左子树高。

例如,假设一个节点的左子树高度为3,右子树高度为2,那么该节点的平衡因子为:3 - 2 = 1。

3.结构体设计
cpp 复制代码
typedef struct AVLNode
{
	ELEMTYPE2 val;
	struct AVLNode* leftchild;
	struct AVLNode* rightchild;
	int height;
}AVLNode;

相应函数

获取高度函数
cpp 复制代码
int Get_Height(AVLNode*node){
    if(node==NULL)return -1;
    return node->height;
}
购买节点函数
cpp 复制代码
AVLNode* AVLBuyNode(int val)
{
	//AVLNode* p = new AVLNode();
	AVLNode* p = (AVLNode*)malloc(sizeof(AVLNode));
	if (p == nullptr)return nullptr;
	p->height = 0;
	p->leftchild = nullptr;
	p->rightchild = nullptr;
	p->val = val;
	return p;
}
更新高度函数
cpp 复制代码
void update_Height(AVLNode* node)
{
	if (node == NULL)return;
	int left = Get_Height(node->leftchild);
	int right = Get_Height(node->rightchild);
	node->height = left > right ? left + 1 : right + 1;
}
平衡因子计算函数
cpp 复制代码
int Get_Blancefactor(AVLNode* node)
{
	if (node == NULL)return 0;
	int left = Get_Height(node->leftchild);
	int right = Get_Height(node->rightchild);
	return left - right;
}
单左旋函数
cpp 复制代码
AVLNode* LL_Rotation(AVLNode * node)
{
	if (node == NULL)return NULL;
	AVLNode* p = node;
	AVLNode* p1 = node->rightchild;
	p->rightchild = p1->leftchild;
	p1->leftchild = p;
	update_Height(p);
	update_Height(p1);
	return p1;
}
单右旋函数
cpp 复制代码
AVLNode* RR_Rotation(AVLNode* node)
{
	if (node == NULL)return NULL;
	AVLNode* p = node;
	AVLNode* p1 = node->leftchild;
	p->leftchild = p1->rightchild;
	p1->rightchild = p;
	update_Height(p);
	update_Height(p1);
	return p1;
}
通用旋转函数
cpp 复制代码
AVLNode* Rotate(AVLNode* Node) {
	if (Node == NULL)return NULL;
	int banlace = Get_Blancefactor(Node);
	if (banlace == 2) {
		int leftbanlance = Get_Blancefactor(Node->leftchild);
		if (leftbanlance == -1) {
			Node->leftchild = LL_Rotation(Node->leftchild);
			return RR_Rotation(Node);
		}
		else if (leftbanlance == 1) {
			return RR_Rotation(Node);
		}
	}
	if (banlace == -2) {
		int rightbanlance = Get_Blancefactor(Node->rightchild);
		if (rightbanlance == -1) {
			return LL_Rotation(Node);
		}
		else if (rightbanlance == 1) {
			Node->rightchild = RR_Rotation(Node->rightchild);
			return LL_Rotation(Node);
		}
	}
	return Node;
}
初始化函数
cpp 复制代码
void Init_AVLtree(AVLTree* pTree)
{
	assert(pTree != nullptr);
	pTree->root = nullptr;
}
插入函数
cpp 复制代码
bool Insert_AVLTree(AVLTree* pTree, ELEMTYPE2 val) {
	pTree->root = Insert_Helper(pTree->root, val);
	return true;
}
AVLNode* Insert_Helper(AVLNode* root, ELEMTYPE2 val) {
	if (root == NULL) {
		AVLNode* p = AVLBuyNode(val);
		return p;
	}
	else if (root->val < val) {
		root->rightchild = Insert_Helper(root->rightchild, val);
	}
	else if (root->val > val) {
		root->leftchild = Insert_Helper(root->leftchild, val);
	}
	update_Height(root);
	root = Rotate(root);
	return root;
}
复制代码
##### 删除函数

```cpp
AVLNode* Delete_Helper(AVLNode* root, ELEMTYPE2 val)
{
	if (root == NULL)return NULL;
	if (root->val > val) {
		root->leftchild = Delete_Helper(root->leftchild, val);
	}
	else if (root->val < val) {
		root->rightchild = Delete_Helper(root->rightchild, val);
	}
	else {
		if (root->leftchild == NULL && root->rightchild == NULL) {
			free(root);
			root = NULL;
			return NULL;
		}
		else if (root->leftchild && root->rightchild) {
			AVLNode* p = root->rightchild;
			while (p->leftchild) {
				p = p->leftchild;
			}
			root->val = p->val;
			root->rightchild=Delete_Helper(root->rightchild, p->val);
		}
		else {
			AVLNode* p1 = root;
			root = root->leftchild;
			if (root->rightchild !=NULL) {
				root = root->rightchild;
			}
			free(p1);
			p1 = NULL;
			return root;
		}
	}
	update_Height(root);
	root = Rotate(root);
	return root;
}
```
复制代码
##### 查找函数

```cpp
AVLNode* Search_BSTree(AVLTree* pTree, ELEMTYPE2 val)
{
	assert(pTree != NULL);
	AVLNode* p1 = pTree->root;
	while (p1 != NULL) {
		if (p1->val < val) {
			p1 = p1->rightchild;
		}
		else if (p1->val > val) {
			p1 = p1->leftchild;
		}
		else {
			return p1;
		}
	}
	return NULL;
}
```
复制代码
##### 打印函数

```cpp
void Show_inorder(AVLTree* root)
{
	assert(root != NULL);
	AVLNode* p1 = root->root;
	stack<AVLNode*>s1;
	while (!s1.empty() || p1 != NULL) {
		while (p1 != NULL) {
			s1.push(p1);
			p1 = p1->leftchild;
		}
		p1 = s1.top();
		s1.pop();
		printf("%d ", p1->val);
		p1 = p1->rightchild;
	}
}
```

AVL 树的时间复杂度与性能

时间复杂度

AVL 树的高度始终为 O (log n)(n 为节点数),因此所有操作的时间复杂度均由树的高度决定:

  • 插入:O (log n)(BST 插入 O (log n) + 回溯更新高度 O (log n) + 旋转 O (1));
  • 删除:O (log n)(BST 删除 O (log n) + 回溯更新高度 O (log n) + 旋转 O (1));
  • 查找:O (log n)(与 BST 一致,无额外开销)。
性能特点
  • 查找性能稳定:无论插入顺序如何,树始终平衡,查找时间复杂度严格 O (log n);
  • 插入 / 删除开销:因需回溯更新高度和旋转,插入 / 删除的常数时间比普通 BST 大,但整体仍为 O (log n);
  • 旋转次数少:插入最多需要 1 次旋转(修复一个失衡节点),删除最多需要 O (log n) 次旋转(最坏情况需回溯到根节点)。

AVL 树的优缺点与适用场景

优点
  1. 绝对平衡:严格保证所有节点的平衡因子绝对值 ≤ 1,查找性能稳定;
  2. 实现简单:旋转逻辑清晰,仅 4 种旋转情况,易于编码实现;
  3. 无性能退化:避免了普通 BST 有序插入时退化为链表的问题。
缺点
  1. 平衡条件严格:为维持绝对平衡,插入 / 删除时频繁旋转,导致插入 / 删除的效率低于红黑树(红黑树是 "近似平衡",旋转次数更少);
  2. 高度更新开销:每个节点需存储高度信息,插入 / 删除后需回溯更新,额外消耗内存和时间;
  3. 不适合高频插入 / 删除场景:若场景中插入 / 删除操作远多于查找,AVL 树的性能不如红黑树。
适用场景
  • 查找操作频繁,插入 / 删除操作较少的场景(如静态数据查询、字典查询);
  • 对查找性能稳定性要求高,不允许性能退化的场景;
  • 数据量中等,无需极致插入 / 删除效率的场景。

核心总结

  1. AVL 树是「绝对平衡的二叉搜索树」,核心通过「平衡因子」和「4 种旋转」维持平衡;
  2. 平衡因子 = 左子树高度 - 右子树高度,需保证所有节点 BF ∈ {-1, 0, 1};
  3. 旋转是失衡修复的核心:LL/RR 为基础旋转,LR/RL 为组合旋转,需根据 BF 判定场景;
  4. 插入流程:BST 插入 → 回溯更新高度 → 检查 BF → 失衡旋转(仅需一次旋转);
  5. 删除流程:BST 删除 → 回溯更新高度 → 检查 BF → 失衡旋转 → 持续回溯到根;
  6. 优点是查找性能稳定(O (log n)),缺点是插入 / 删除旋转频繁,适合查找密集型场景。