数据结构 | 二叉平衡搜索树

一、二叉平衡搜索树定义

1. 标准定义

二叉平衡搜索树(AVL)是在BST 基础上增加平衡约束 的特殊有序二叉树,由 n(n≥0)个节点构成。在完全保留BST有序规则的前提下,通过高度平衡机制限制树的倾斜程度,解决普通BST频繁增删后退化、效率暴跌的问题,始终维持**O(logn)**的稳定操作效率。

2. 核心性质

整棵树需要同时满足BST有序规则与AVL平衡规则,三大硬性性质缺一不可:

  • 基础有序:任意节点,其左子树 所有节点关键字 < 当前节点关键字 < 右子树所有节点关键字,节点数值不重复
  • 递归平衡:整棵树是AVL树,则所有子树均为AVL树
  • 高度平衡:任意节点的左右子树高度差绝对值 ≤ 1

补充考点:AVL树完全继承BST遍历特性,中序 遍历输出序列一定是升序,是区分普通不平衡BST的基础特征。

3. 平衡因子

平衡因子是判定节点是否平衡的核心依据,计算规则:平衡因子 = 左子树高度 - 右子树高度

合法取值仅有三个:-1、0、1 ;若平衡因子为 ±2 ,代表当前节点失衡,必须通过旋转操作修复平衡。

二、AVL 存储结构

1. 存储设计思路

普通BST二叉链表仅存储孩子指针,无高度记录,无法判定平衡状态,极易退化斜树。本文采用优化二叉链表 实现,舍弃冗余的parent父指针,新增height高度域,通过递归维护节点高度、计算平衡因子,简化旋转与平衡修复逻辑,代码更简洁高效。

2. 结构体定义

分为两层结构体:AVLNode为单个节点单元,存储数据、孩子指针与高度;AVLTree封装根节点指针,统一管理整棵树,适配C++语法规范。

cpp 复制代码
typedef int ELEMTYPE;
// AVL树节点结构体
struct AVLNode {
	ELEMTYPE data;
	AVLNode* leftchild;
	AVLNode* rightchild;
	int height;
	// C++构造函数,初始化节点默认值
	AVLNode() : data(0), leftchild(nullptr), rightchild(nullptr), height(0) {}
};
// AVL树管理结构体
struct AVLTree {
	AVLNode* root;
	AVLTree() : root(nullptr) {}
};

3. 存储结构优缺点

优点:无需维护双向父指针,递归实现逻辑简洁;高度域可快速计算平衡因子,精准判定节点失衡;四类旋转操作可完美修复所有失衡场景。

缺点:每次增删节点后,必须回溯更新祖先节点高度、校验平衡状态,相比普通BST,节点维护开销更大。

适配范围:高频查找、动态增删的业务场景,对算法效率稳定性要求高的场景;静态少量数据仅遍历场景,优先使用普通BST。

三、底层工具函数

工具函数为增删改查业务操作提供底层支撑,仅由内部函数调用,不对外暴露,统一采用C++语法实现。

1. BuyNode 创建新节点

实现思路:通过C++ new动态分配节点内存,调用构造函数自动初始化左右孩子指针置空、高度初始化为0,返回新节点地址。

cpp 复制代码
AVLNode* BuyNode() {
	AVLNode* pnewnode = new AVLNode;
	return pnewnode;
}

2. Get_Height 获取节点高度

实现思路:空节点无高度,统一定义为**-1**;非空节点返回自身height成员值,为平衡因子计算提供数据支撑。

cpp 复制代码
int Get_Height(AVLNode* node) {
	if (node == nullptr)
		return -1;
	return node->height;
}

3. Updata_Height 更新节点高度

实现思路:断言校验节点非空,获取左右子树高度,取最大值**+1**,刷新当前节点高度,保证高度数据实时准确。

cpp 复制代码
void Updata_Height(AVLNode* node) {
	assert(node != nullptr);
	int lh = Get_Height(node->leftchild);
	int rh = Get_Height(node->rightchild);
	node->height = lh > rh ? lh + 1 : rh + 1;
}

4. Get_BalanceFactor 获取平衡因子

实现思路:空节点平衡因子默认为0;非空节点通过左右子树高度差值计算,判定节点平衡状态。

cpp 复制代码
int Get_BalanceFactor(AVLNode* node) {
	if (node == nullptr)
		return 0;
	return Get_Height(node->leftchild) - Get_Height(node->rightchild);
}

5. Left_Rotate 左旋操作

实现思路:适配RR、RL型失衡,将当前失衡节点下沉为右孩子的左子节点,重定向指针后更新相关节点高度。

cpp 复制代码
AVLNode* Left_Rotate(AVLNode* node) {
	assert(node != nullptr);
	AVLNode* child = node->rightchild;
	AVLNode* grandchild = child->leftchild;
	// 完成指针重定向
	node->rightchild = grandchild;
	child->leftchild = node;
	// 旋转后更新高度
	Updata_Height(node);
	Updata_Height(child);

	return child;
}

6. Right_Rotate 右旋操作

实现思路:适配LL、LR型失衡,将当前失衡节点下沉为左孩子的右子节点,重定向指针后更新相关节点高度。

cpp 复制代码
AVLNode* Right_Rotate(AVLNode* node) {
	assert(node != nullptr);
	AVLNode* child = node->leftchild;
	AVLNode* grandchild = child->rightchild;
	// 完成指针重定向
	node->leftchild = grandchild;
	child->rightchild = node;
	// 旋转后更新高度
	Updata_Height(node);
	Updata_Height(child);

	return child;
}

7. Insert_Adjust 插入平衡调整

实现思路:根据节点平衡因子,精准判定四类插入失衡场景,匹配单旋/双旋方案修复平衡;插入失衡仅需单次调整即可恢复全局平衡。

cpp 复制代码
AVLNode* Insert_Adjust(AVLNode* node) {
	int bal = Get_BalanceFactor(node);
	// LL型失衡:单次右旋
	if (bal == 2 && Get_BalanceFactor(node->leftchild) == 1) {
		return Right_Rotate(node);
	}
	// LR型失衡:先左旋左孩子,再右旋当前节点
	if (bal == 2 && Get_BalanceFactor(node->leftchild) == -1) {
		node->leftchild = Left_Rotate(node->leftchild);
		return Right_Rotate(node);
	}
	// RR型失衡:单次左旋
	if (bal == -2 && Get_BalanceFactor(node->rightchild) == -1) {
		return Left_Rotate(node);
	}
	// RL型失衡:先右旋右孩子,再左旋当前节点
	if (bal == -2 && Get_BalanceFactor(node->rightchild) == 1) {
		node->rightchild = Right_Rotate(node->rightchild);
		return Left_Rotate(node);
	}
	return node;
}

8. Delete_Adjust 删除平衡调整

实现思路:兼容删除连锁失衡特性,相比插入调整,平衡因子为0时仍需修复,覆盖所有删除失衡场景,逐层回溯修复祖先节点失衡。

cpp 复制代码
AVLNode* Delete_Adjust(AVLNode* node) {
	int bal = Get_BalanceFactor(node);
	// LL、L平衡型
	if (bal == 2) {
		if (Get_BalanceFactor(node->leftchild) == 1 || Get_BalanceFactor(node->leftchild) == 0) {
			return Right_Rotate(node);
		}
		if (Get_BalanceFactor(node->leftchild) == -1) {
			node->leftchild = Left_Rotate(node->leftchild);
			return Right_Rotate(node);
		}
	}
	// RR、R平衡型
	if (bal == -2) {
		if (Get_BalanceFactor(node->rightchild) == -1 || Get_BalanceFactor(node->rightchild) == 0) {
			return Left_Rotate(node);
		}
		if (Get_BalanceFactor(node->rightchild) == 1) {
			node->rightchild = Right_Rotate(node->rightchild);
			return Left_Rotate(node);
		}
	}
	return node;
}

四、AVL 五大基础操作

1. 初始化二叉平衡树

实现思路:断言校验树结构体指针合法,将树根指针置空,初始化空的AVL树。

cpp 复制代码
void Init_AVLTree(AVLTree* pTree) {
	assert(pTree != nullptr);
	pTree->root = nullptr;
}

2. 插入节点

实现思路:遵循BST有序规则查找插入位置,递归创建新节点;回溯过程更新节点高度,校验并修复失衡,保证插入后树始终平衡。

递归核心逻辑:

  1. 空节点位置新建节点,作为叶子节点插入
  2. 数值重复直接返回,AVL树继承BST数值唯一规则
  3. 递归回溯更新每层节点高度
  4. 调用插入调整函数修复失衡
cpp 复制代码
// 递归插入底层实现
AVLNode* Insert_Recursion(AVLNode* root, ELEMTYPE val) {
	// 找到插入位置,新建节点
	if (root == nullptr) {
		AVLNode* pnewnode = BuyNode();
		pnewnode->data = val;
		return pnewnode;
	}
	// 按BST规则递归插入
	if (val > root->data) {
		root->rightchild = Insert_Recursion(root->rightchild, val);
	}
	else if (val < root->data) {
		root->leftchild = Insert_Recursion(root->leftchild, val);
	}
	else {
		// 数值重复,插入失败
		return root;
	}
	// 更新当前节点高度并修复平衡
	Updata_Height(root);
	return Insert_Adjust(root);
}

// 对外插入接口
bool Insert_AVL(AVLTree* pTree, ELEMTYPE val) {
	assert(pTree != nullptr);
	pTree->root = Insert_Recursion(pTree->root, val);
	return true;
}

3. 递归查找节点

实现思路:完全复用BST二分查找逻辑,从根节点出发,依据数值大小规则遍历左右子树,匹配返回节点,无匹配返回空。

cpp 复制代码
AVLNode* Search_AVL(AVLNode* root, ELEMTYPE val) {
	if (root == nullptr)
		return nullptr;
	if (val > root->data) {
		return Search_AVL(root->rightchild, val);
	}
	else if (val < root->data) {
		return Search_AVL(root->leftchild, val);
	}
	else {
		return root;
	}
}

4. 删除节点

(1)三类删除场景对比

零孩子(叶子节点):直接释放节点,无后续指针维护,逻辑最简单。

单孩子(仅左/右子树):唯一子节点直接顶替待删节点位置,继承父节点关联。

双孩子(左右子树均存在) :无法直接删除,采用狸猫换太子方案,转换为零/单分支删除场景。

(2)狸猫换太子核心原理

AVL树继承BST节点特性,任意节点的直接前驱/后继最多仅有单侧子树。删除双分支节点时,取**左子树最大值(直接前驱)**覆盖待删节点数据,将删除目标转移至前驱节点,规避复杂的双分支替换逻辑。

(3)AVL删除核心特点

普通BST删除无需平衡调整,而AVL树删除节点会导致祖先链连锁失衡,必须从删除位置回溯至根节点,逐层更新高度、修复平衡。

cpp 复制代码
// 递归删除底层实现
AVLNode* Delete_Recursion(AVLNode* root, ELEMTYPE val) {
	if (root == nullptr)
		return nullptr;
	// 递归查找待删除节点
	if (val > root->data) {
		root->rightchild = Delete_Recursion(root->rightchild, val);
	}
	else if (val < root->data) {
		root->leftchild = Delete_Recursion(root->leftchild, val);
	}
	else {
		// 找到待删除节点,处理三类删除场景
		if (root->leftchild != nullptr && root->rightchild != nullptr) {
			// 双分支:狸猫换太子,替换前驱节点
			AVLNode* cat = root->leftchild;
			while (cat->rightchild != nullptr)
				cat = cat->rightchild;
			root->data = cat->data;
			root->leftchild = Delete_Recursion(root->leftchild, cat->data);
		}
		else {
			// 零/单分支节点
			AVLNode* child = root->leftchild != nullptr ? root->leftchild : root->rightchild;
			delete root;
			root = nullptr;
			return child;
		}
	}
	// 回溯更新高度、修复平衡
	Updata_Height(root);
	return Delete_Adjust(root);
}

// 对外删除接口
bool Delete_AVL(AVLTree* pTree, ELEMTYPE val) {
	assert(pTree != nullptr);
	pTree->root = Delete_Recursion(pTree->root, val);
	return true;
}

5. 中序遍历打印

实现思路:采用非递归单栈法模拟递归遍历,遵循左根右顺序,依托AVL树BST有序特性,遍历结果严格升序。

cpp 复制代码
void Show_InOrder_AVL(AVLNode* root) {
	assert(root != nullptr);
	std::stack<AVLNode*> st;
	st.push(root);
	bool tag = true;

	while (!st.empty()) {
		while (tag && st.top()->leftchild != nullptr) {
			st.push(st.top()->leftchild);
		}
		AVLNode* tmp = st.top();
		st.pop();
		printf("%d ", tmp->data);

		if (tmp->rightchild != nullptr) {
			st.push(tmp->rightchild);
			tag = true;
		}
		else {
			tag = false;
		}
	}
	printf("\n");
}

五、AVL 失衡类型

1. 四大失衡场景与修复方案

LL型失衡 :左子树高度过高,左孩子左子树偏重,采用单次右旋修复。

LR型失衡 :左子树高度过高,左孩子右子树偏重,采用先左旋、后右旋双旋修复。

RR型失衡 :右子树高度过高,右孩子右子树偏重,采用单次左旋修复。

RL型失衡 :右子树高度过高,右孩子左子树偏重,采用先右旋、后左旋双旋修复。

2. 插入与删除平衡核心差异

插入操作 :新增节点仅会造成单条祖先链一处失衡,一次旋转即可全局平衡,无需多层回溯。

删除操作 :节点删除会改变子树高度,引发多层祖先节点连锁失衡,必须逐层回溯至根节点,多次校验修复。

六、总结

1. 结构本质区别

普通二叉树无数值约束,仅限制节点度≤2;BST具备有序规则但无平衡约束,易退化斜树;AVL树基于BST拓展高度平衡机制,在有序基础上严格限制子树高度差,彻底解决树结构退化问题。

2. 遍历特性区别

普通二叉树所有遍历序列无序;BST与AVL树遍历特性一致,仅中序遍历输出严格升序,先、后序遍历无有序特性,无法体现有序属性。

3. 性能与工程选型

普通BST效率不稳定,平衡状态O(logn)、斜树退化O(n);AVL树全程维持平衡形态,增删查稳定O(logn)。刷题简单遍历场景可选普通BST,高频动态增删、高稳定性检索场景优先选用AVL树。

三类树形核心对比表

对比维度 普通二叉树 二叉搜索树(BST) AVL平衡搜索树
节点规则 无数值大小约束 左小右大、数值不重复 满足BST规则+高度平衡
平衡特性 无平衡约束 无平衡,易斜树退化 高度差≤1,严格平衡
中序遍历 序列无序 严格升序 严格升序
操作效率 O(n) O(logn)~O(n) 稳定O(logn)
核心用途 简单层级存储、遍历 基础排序、动态检索 高性能、稳定数据检索
相关推荐
kuonyuma1 小时前
java之动态代理
java·开发语言
KouFiee1 小时前
同名隐藏基础
开发语言·c++
z落落1 小时前
C# Task WaitAll、WaitAny、WhenAll、WhenAny
开发语言·c#
兰令水1 小时前
hot100【acm版】【2026.7.11/12打卡-java版本】
java·开发语言·数据结构·算法·职场和发展
从零开始的代码生活_2 小时前
C++ string 详解:常用接口、字符串算法与深拷贝实现
开发语言·c++·后端·学习·算法
m0_738120722 小时前
PHP代码审计基础——面向对象(四)
android·开发语言·网络·安全·github·php
2601_963771372 小时前
10 Best PHP Media CMS and Scripts for Web Creators in 2026
开发语言·前端·php
阿里嘎多学长3 小时前
2026-07-09 GitHub 热点项目精选
开发语言·程序员·github·代码托管
带娃的IT创业者3 小时前
监控并非安全:当隐私成为技术的祭品
java·开发语言·安全·数据安全·监控·隐私保护·加密技术