当前位置:网站首页>AVL树的实现
AVL树的实现
2022-06-23 07:17:00 【s_persist】
文章目录
一、AVL树的概念
二叉搜索树虽可以缩短查找的效率,但如果数据有序或接近有序二叉搜索树将退化为单支树,查找元素相当于在顺序表中搜索元素,效率低下。
因此,两位俄罗斯的数学家G.M.Adelson-Velskii和E.M.Landis在1962年发明了一种解决上述问题的方法:当向二叉搜索树中插入新结点后,如果能保证每个结点的左右子树高度之差的绝对值不超过1(需要对树中的结点进行调整),即可降低树的高度,从而减少平均搜索长度。
一棵AVL树或者是空树,或者是具有以下性质的二叉搜索树:
- 它的左右子树都是AVL树
- 左右子树高度之差(简称平衡因子)的绝对值不超过1(-1/0/1)
AVL的平衡不一定需要平衡因子,使用平衡因子只是控制平衡的一种方式。如果一棵二叉搜索树是高度平衡的,它就是AVL树。如果它有n个结点,其高度可保持在 logN (2为底),搜索时间复杂度O(log(N))。
二、AVL树的操作
2.1 AVL节点的定义
使用三叉链,多存储一个父节点,方便控制平衡,KV模型,存储一个pair。
template<class K, class V>
struct AVLTreeNode
{
AVLTreeNode<K, V>* _left;
AVLTreeNode<K, V>* _right;
AVLTreeNode<K, V>* _parent;
pair<K, V> _kv;
int _bf; // 平衡因子
AVLTreeNode(const pair<K, V>& kv)
:_left(nullptr),
_right(nullptr),
_parent(nullptr),
_kv(kv),
_bf(0)
{
}
};
template<class K, class V>
class AVLTree
{
typedef AVLTreeNode<K, V> Node;
public:
AVLTree():_root(nullptr)
{
}
private:
Node* _root;
};
2.2 AVL的插入
AVL树就是在二叉搜索树的基础上引入了平衡因子,因此AVL树也可以看成是二叉搜索树。那么AVL树的插入
过程可以分为两步:
- 按照二叉搜索树的方式插入新节点
- 调整节点的平衡因子
新节点插入后,AVL树的平衡性可能会遭到破坏,此时就需要更新平衡因子,并检测是否破坏了
AVL树的平衡性
cur插入后,pParent的平衡因子一定需要调整,在插入之前,parent
的平衡因子分为三种情况:-1,0, 1, 分以下两种情况:
- 如果cur插入到parent的左侧,只需给parent的平衡因子-1即可
- 如果cur插入到parent的右侧,只需给parent的平衡因子+1即可
此时:parent的平衡因子可能有三种情况:0,正负1, 正负2
- 如果arent的平衡因子为0,说明插入之前pParent的平衡因子为正负1,插入后被调整成0,此时满足AVL树的性质,插入成功
- 如果parent的平衡因子为正负1,说明插入前parent的平衡因子一定为0,插入后被更新成正负1,此时以pParent为根的树的高度增加,需要继续向上更新
- 如果parent的平衡因子为正负2,则parent的平衡因子违反平衡树的性质,需要对其进行旋转处理.
bool Insert(const pair<K, V>& kv){
if (_root == nullptr)
{
_root = new Node(kv);
return true;
}
Node* parent = nullptr;
Node* cur = _root;
while (cur)
{
if (kv.first > cur->_kv.first)
{
parent = cur;
cur = cur->_right;
}
else if (kv.first < cur->_kv.first)
{
parent = cur;
cur = cur->_left;
}
else return false; // 已经有了
}
// 插入结点
cur = new Node(kv);
if (kv.first > parent->_kv.first)
{
parent->_right = cur;
cur->_parent = parent;
}
else
{
parent->_left = cur;
cur->_parent = parent;
}
// 更新平衡因子
// 1. 更新从新增结点到根节点的路径上的结点的平衡因子的值
// 2. 如果出现不平衡需要旋转
while (parent) // 每次判别父结点是否平衡
{
if (cur == parent->_left)
parent->_bf--;
else
parent->_bf++;
// 父节点的平衡因子改变,需要做不同的处理
if (parent->_bf == 0)
break;
else if (parent->_bf == 1 || parent->_bf == -1)
{
cur = parent;
parent = parent->_parent;
}
else if (parent->_bf == 2 || parent->_bf == -2)
{
// 出现不平衡需要旋转
if (parent->_bf == 2 && cur->_bf == 1)
RotateL(parent);// 左旋
else if (parent->_bf == -2 && cur->_bf == -1)
RotateR(parent); // 右旋
else if (parent->_bf == 2 && cur->_bf == -1)
{
// 先右后左
RotateRL(parent);
}
else if (parent->_bf == -2 && cur->_bf == 1)
{
// 先左后右
RotateLR(parent);
}
else
assert(false); // 报错
break;
}
else
return false;
}
}
2.3 AVL树的旋转
2.3.1 左单旋转

代码实现
void RotateL(Node* ptr){
// ptr->bf == 2 && ptr->_right->bf == 1 --- 左单旋
Node* subR = ptr->_right;
Node* subRL = subR->_left;
ptr->_right = subRL;
if (subRL){
// subRL存在
subRL->_parent = ptr;
}
subR->_left = ptr;
if (ptr == _root ){
// ptr为根节点
subR->_parent = nullptr;
ptr->_parent = subR;
_root = subR;
}
else {
Node* ptrParent = ptr->_parent;
subR->_parent = ptrParent;
if (ptrParent->_left == ptr) {
ptrParent->_left = subR;
}
else {
ptrParent->_right = subR;
}
ptr->_parent = subR;
}
// 修改平衡因子
ptr->_bf = subR->_bf = 0;
}
2.3.2 右单旋转
右旋其实是左旋的镜像。

void RotateR(Node* ptr) {
Node* subL = ptr->_left;
Node* subLR = subL->_right;
ptr->_left = subLR;
if (subLR) {
subLR->_parent = ptr;
}
subL->_right = ptr;
if (ptr == _root) {
ptr->_parent = subL;
subL->_parent = nullptr;
_root = subL;
}
else {
Node* ptrParent = ptr->_parent;
subL->_parent = ptrParent;
if (ptrParent->_left == ptr) {
ptrParent->_left = subL;
}
else {
ptrParent->_right = subL;
}
ptr->_parent = subL;
}
ptr->_bf = subL->_bf = 0;
}
2.3.3 先左后右双旋转
判断是否需要两次旋转,就看发生不平衡点的左右子树的平衡因子,与父节点的平衡因子符合相反(即高低的方向相反),这是需要使用双旋。
父节点为 -2,左子树为 1 ------- 先左旋再右旋
父节点为 2, 右子树位 -1 ------ 先右旋再左旋
先旋的都是子树,后旋的是父节点
代码很简单,但是需要调整最终旋转后的ptr的平衡因子值。
void RotateLR(Node* ptr)
{
Node* subL = ptr->_left;
Node* subLR = subL->_right;
// 如果需要这样选择,那么ptr结点和sub结点的平衡因子是固定的,只有subLR是不确定的,需要判断
// 这里记录下插入某一个结点后,subLR的平衡因子
int bf = subLR->_bf;
// 先左后右, 由于这两种情况的调整后,三个结点的平衡因子都被置为0,但不一定都是0,需要调整
RotateL(subL);
RotateR(ptr);
// 调整平衡因子, 只会有这三种情况
if (bf == -1) {
// 图中所示情况
ptr->_bf = 1;
subL->_bf = 0;
subLR->_bf = 0;
}
else if (bf == 1) {
// 新节点在左边
ptr->_bf = 0;
subL->_bf = -1;
subLR->_bf = 0;
}
else if (bf == 0) {
// 两边高度相等
ptr->_bf = 0;
subL->_bf = 0;
subLR->_bf = 0;
}
else {
assert(false); // 说明早就错了
}
}
2.3.3 先右后左双旋转


void RotateRL(Node* ptr)
{
Node* subR = ptr->_right;
Node* subRL = ptr->_left;
int bf = subRL->_bf;
// 先右后左
RotateR(subR);
RotateL(ptr);
if (bf == 1) {
//图中情况
ptr->_bf = -1;
subR->_bf = 0;
subRL->_bf = 0;
}
else if (bf == -1) {
// 加在了左边
ptr->_bf = 0;
subR->_bf = 1;
subRL->_bf = 0;
}
else if (bf == 0) {
// 都等高
ptr->_bf = 0;
subR->_bf = 0;
subRL->_bf = 0;
}
else {
assert(false); // 说明早就错了
}
}
2.4 AVL树的验证
AVL树是在二叉搜索树的基础上加入了平衡性的限制,因此要验证AVL树,可以分两步:
- 验证其为二叉搜索树
如果中序遍历可得到一个有序的序列,就说明为二叉搜索树 - 验证其为平衡树
每个节点子树高度差的绝对值不超过1,并且节点的平衡因子是否计算正确
void InOrder()
{
_InOrder(_root);
}
void _InOrder(Node* root)
{
if (root == nullptr)
return;
_InOrder(root->_left);
cout << root->_kv.first << " : " << root->_kv.second << endl;
_InOrder(root->_right);
}
bool IsBalance()
{
return _IsBalance(_root);
}
int Height(Node* root)
{
if (root == nullptr) return 0;
return max(Height(root->_left), Height(root->_right)) + 1;
}
bool _IsBalance(Node* root)
{
if (root == nullptr)
return true;
// 检查左右高度差
int leftHeight = Height(root->_left);
int rightHeight = Height(root->_right);
if (rightHeight - leftHeight != root->_bf)
{
cout << root->_kv.first << "现在是:" << root->_bf << endl;
cout << root->_kv.first << "应该是:" << rightHeight - leftHeight << endl;
return false;
}
return abs(rightHeight - leftHeight) < 2
&& _IsBalance(root->_left)
&& _IsBalance(root->_right);
}
void TestAVLTree()
{
AVLTree<int, int> t;
//int a[] = { 16, 3, 7, 11, 9, 26, 18, 14, 15 }; // 测试是否为二叉搜索树
int a[] = {
16, 3, 7, 2, 5, 4}; // 测试先左后右双旋,平衡因子的正确性
// int a[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14 }; // 测试先右后左双旋的平衡因子是否正确
for (auto e : a)
{
t.Insert(make_pair(e, e));
cout << "Insert" << e << ":" << t.IsBalance() << endl;
}
t.InOrder();
cout << t.IsBalance() << endl;
}
边栏推荐
- Download the OSS file and modify the file name
- [pyqt5 series] modify the counter to realize control
- 某年某月某公司的面试题(1)
- Matlab随机波动率SV、GARCH用MCMC马尔可夫链蒙特卡罗方法分析汇率时间序列
- Distributed ID generation
- [* * * array * * *]
- 传智教育 | 多人协作开发出现代码冲突,如何合并代码?
- [pit stepping record] a pit where the database connection is not closed and resources are released
- Unity to wechat applet games
- Decoding and practice of cmaf Technology
猜你喜欢

3dmax插件开发环境配置及FileExport和Utilities模板测试

20bn Jester complete dataset Download

一篇文章学会er图绘制

To conquer salt fields and vegetable fields with AI, scientific and technological innovation should also step on the "field"

Eureka service registration and discovery
![[interface automation] software testing the core skills of salary increase to increase salary by 200%](/img/22/be8c5c922307225c34f6205f189c33.png)
[interface automation] software testing the core skills of salary increase to increase salary by 200%

The sandbox has reached a cooperation with football player to bring popular football cartoons and animation into the metauniverse

MySQL on duplicate key and PgSQL on conflict (primary key) handle primary key conflicts

How bootstrap clears floating styles

Using the for loop to output an alphabetic triangle
随机推荐
Eureka服务注册与发现
2022山东大学软件学院软件项目管理期末考试(回忆版)
The sandbox has reached a cooperation with football player to bring popular football cartoons and animation into the metauniverse
《一周的朋友》
2.概率论-概率论公理
Matlab随机波动率SV、GARCH用MCMC马尔可夫链蒙特卡罗方法分析汇率时间序列
Qt工程报错:-1: error: Cannot run compiler ‘clang++‘. Output:mingw32-make.exe
在线文本过滤小于指定长度工具
Difference between char and varchar
Eureka service registration and discovery
Product axure9 (English version), prototype design and production pull-down secondary menu
Intelligence Education - how to merge codes when code conflicts occur in multi person collaborative development?
unity转微信小程序小游戏
Mathematical knowledge: fast power inverse element - fast power
Principle of skip table
电脑如何安装MySQL?
How to quickly and gracefully download large files from Google cloud disk (II)
Unity to wechat applet games
WPS for thesis writing installs MathType plug-in to write mathematical formulas
The Sandbox 与《足球小将》达成合作,将流行的足球漫画及动画带入元宇宙

