AVL树模拟

1.概念

虽然二叉搜索树可以缩短查找的效率,但如果数据有序或者接近有序时二叉搜索树树将退化为单支树,查找元素相当于在顺序表中搜索元素,效率低下。AVL
树是具有一下性质的二叉搜索树:        

        1.它的左右子树都是AVL树

        2.左右子树的高度差的绝对值不超过1

如果一个二叉搜索树是高度平衡的,它就是AVL树。如果它有n个节点,其高度可保持在log N,搜索时间复杂度为O(log N);

2.节点定义

template<class T>
struct AVLTreeNode
{AVLTreeNode(T key):_bf(0), _key(key){}AVLTreeNode* _left = nullptr;AVLTreeNode* _right = nullptr;AVLTreeNode* _parent = nullptr;int _bf;					//平衡因子T _key;
};

3.AVL插入

AVL树的插入就是在二叉搜索树的基础上引入了平衡因子,因此AVL树也可以看成是二叉搜索树。

AVL树的插入过程可以分为两部分:

        1.按照二叉搜索树的方式插入新节点

        2.调整平衡因子

3.1 按照二叉搜索树的方式插入新的节点

 

bool Insert(T key)
{Node* newnode = new Node(key);if (_root == nullptr){_root = newnode;return true;}Node* cur = _root;Node* parent = nullptr;//寻找插入位置while (cur){if (key > cur->_key){parent = cur;cur = cur->_right;}else if (key < cur->_key){parent = cur;cur = cur->_left;}else return false;}//插入新的节点newnode->_parent = parent;if (key < parent->_key) parent->_left = newnode;else parent->_right = newnode;
}

3.2调整平衡因子

新节点插入后,AVL树的平衡性可能遭到破坏,因此就需要更新平衡因子,并检测是否破坏了AVL树的平衡性

当newnode插入后,parent的平衡因子一点需要调整,在插入之前parent的平衡因子分为三种情况:-1,0,1。

调整方式分为以下两种:

        当新节点插入到parent左侧时,parent平衡因子-1;

        当新节点插入到parent右侧时,parent平衡因子+1;

此时parent的平衡因子有以下三种情况:0,正负1,正负2

        1.如果此时的平衡因子为0,说明插入新的节点后parent平衡了,满足AVL树的性质,无需继续向上调整。

        2.如果此时平衡因子为正负1,说明插入新的节点后parent为根的树高度增加,需要继续向上调整。

        3.如果此时平衡因子为正负2,说明parent违反了AVL树的性质,需要对其经行旋转处理。

cur = newnode;
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){//...}else{//...}break;}else{cout << "error: _bf";break;}
}

4. AVL树的旋转

 上图在插入前,AVL树是平衡的,新节点插入到30的左子树(注意:此处不是左孩子)中,30左 子树增加了一层,导致以60为根的二叉树不平衡。

要让60平衡,只能将60左子树的高度减少一层,右子树增加一层,  即将左子树往上提,这样60转下来,因为60比30大,只能将其放在30的右子树,而如果30有右子树,右子树根的值一定大于30,小于60,只能将其放在60的左子树,旋转完成后,更新节点 的平衡因子即可。

在旋转过程中,有以下几种情况需要考虑:  1. 30节点的右孩子可能存在,也可能不存在  2. 60可能是根节点,也可能是子树如果是根节点,旋转完成后,要更新根节点如果是子树,可能是某个节点的左子树,也可能是右子树

a.右单旋


void _RotateR(Node* pParent)
{// pSubL: pParent的左孩子// pSubLR: pParent左孩子的右孩子,注意:该PNode* pSubL = pParent->_pLeft;PNode* pSubLR = pSubL->_pRight;// 旋转完成之后,30的右孩子作为双亲的左孩子pParent->_pLeft = pSubLR;// 如果30的左孩子的右孩子存在,更新亲双亲if (pSubLR) pSubLR->_pParent = pParent;// 60 作为 30的右孩子// 因为60可能是棵子树,因此在更新其双亲前必须先保存60的双亲PNode pPParent = pParent->_pParent;// 更新60的双亲pParent->_pParent = pSubL;// 更新30的双亲pSubL->_pParent = pPParent;// 如果60是根节点,根新指向根节点的指针if (NULL == pPParent){_pRoot = pSubL;pSubL->_pParent = NULL;}else{// 如果60是子树,可能是其双亲的左子树,也可能是右子树if (pPParent->_pLeft == pParent)pPParent->_pLeft = pSubL;elsepPParent->_pRight = pSubL;}// 根据调整后的结构更新部分节点的平衡因子pParent->_bf = pSubL->_bf = 0;
}

b.左单旋 

 

具体细节与右单旋一致。

void _RotateL(Node* pParent)
{Node* RSub = pParent->_right;Node* RSubL = RSub->_left;Node* pPParent = pParent->_parent;if (RSubL) RSubL->_parent = pParent;pParent->_right = RSubL;RSub->_left = pParent;pParent->_parent = RSub;RSub->_parent = pPParent;if (pParent == _root) _root = RSub;else{if (pPParent->_left == pParent) pPParent->_left = RSub;else pPParent->_right = RSub;}//更新平衡因子RSub->_bf = pParent->_bf = 0;
}

c.先左旋在右旋

将双旋变成单旋后再旋转,即:先对30进行左单旋,然后再对90进行右单旋,旋转完成后再 考虑平衡因子的更新。

 

// 旋转之前,60的平衡因子可能是-1/0/1,旋转完成之后,根据情况对其他节点的平衡因子进
//行调整
void _RotateLR(PNode pParent)
{PNode pSubL = pParent->_pLeft;PNode pSubLR = pSubL->_pRight;// 旋转之前,保存pSubLR的平衡因子,旋转完成之后,需要根据该平衡因子来调整其他节//点的平衡因子int bf = pSubLR->_bf;// 先对30进行左单旋_RotateL(pParent->_pLeft);// 再对90进行右单旋_RotateR(pParent);if (1 == bf)pSubL->_bf = -1;else if (-1 == bf)pParent->_bf = 1;
}

d. 先右旋在左旋

void _RotateRL(Node* pParent)
{Node* RSub = pParent->_right;Node* RSubL = RSub->_left;int bf = RSubL->_bf;_RotateR(RSub);_RotateL(pParent);RSub->_bf = 0;if (bf == 1){RSub->_bf = 0;pParent->_bf = -1;}else if (bf == -1){pParent->_bf = 0;RSub->_bf = 1;}else{RSub->_bf = pParent->_bf = 0;}
}

总结:

加入以parent为根的子树不平衡,即以parent的平衡因子为2或-2,分别考虑以下情况

        1.parent的平衡因子为2,说明parent的右子树高,设parent的右子树的根为RSub

                当RSub的平衡因子为1时,执行左单旋,

                当RSub的平衡因子为-1时,执行左右双旋。

        2.parent的平衡因子为-2 ,说明parent的左子树高,设parent的左子树的根为LSub

                当LSub的平衡因子为-1时,执行右单旋。

                当LSub的平衡因子为1时,执行做单旋。

当旋转接完成后,parent为根的子树高度已经降低,以及平衡,无需向上更新。

5.AVL树的验证

AVL树实在二叉搜索树的基础上加了平衡性的限制,因此要验证AVL树可以分为两步

        1.验证其为二叉搜索树

                如果中序遍历结果为有序,则为二叉搜索树

        2.验证其为平衡树

                1.每个子树高度差的绝对值不超过1

                2.节点平衡因子正确

void InOrder()
{_InOrder(_root);
}int GETHeight()
{return _GETHeight(_root);
}bool IsBalance()
{return _isBalance(_root);
}	bool _isBalance(Node* root)
{if (root->_bf >= 2 || root->_bf <= -1) return false;int HeightLeft = _GETHeight(root->_left);int HeightRight = _GETHeight(root->_right);if (abs(HeightRight - HeightLeft) > 1) return false;return _isBalance(root->_left) && _isBalance(root->_right);
}
int _GETHeight(Node* root)
{if (root == nullptr) return 0;return max(_GETHeight(root->_left), _GETHeight(root->_right)) + 1;
}
void _InOrder(Node* root)
{if (root == nullptr) return;_InOrder(root->_left);cout << root->_key << " ";_InOrder(root->_right);
}

测试代码 

void test_AVL01()
{int a[] = { 8, 3, 1, 10, 6, 4, 7, 14, 13 };//int a[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14 };AVLTree<int> t1;for (auto e : a){t1.Insert(e);//cout << "Insert:" << e << "->" << t1.IsBalance() << endl;}cout << t1.GETHeight() << endl;t1.InOrder();//cout << t1.IsBalance() << endl;
}

6.AVL树模拟代码

#pragma oncetemplate<class T>
struct AVLTreeNode
{AVLTreeNode(T key):_bf(0), _key(key){}AVLTreeNode* _left = nullptr;AVLTreeNode* _right = nullptr;AVLTreeNode* _parent = nullptr;int _bf;					//平衡因子T _key;
};template<class T>
class AVLTree
{typedef AVLTreeNode<T> Node;
public:bool Insert(T key){Node* newnode = new Node(key);if (_root == nullptr){_root = newnode;return true;}Node* cur = _root;Node* parent = nullptr;//寻找插入位置while (cur){if (key > cur->_key){parent = cur;cur = cur->_right;}else if (key < cur->_key){parent = cur;cur = cur->_left;}else return false;}//插入新的节点newnode->_parent = parent;if (key < parent->_key) parent->_left = newnode;else parent->_right = newnode;//更新平衡因子//插入后AVL树平衡,无需调整//插入后AVL树高度增加,继续向上调整cur = newnode;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){//左单旋if (parent->_right->_bf == 1){_RotateL(parent);}else{_RotateRL(parent);}}else{//右单旋if (parent->_left->_bf == -1){_RotateR(parent);}else{_RotateLR(parent);}}break;}else{cout << "error: _bf";break;}}return true;}Node* Find(const T& key){Node* cur = _root;while (cur){if (key > cur->_key) cur = cur->_right;else if (key < cur->_key) cur = cur->_left;else return cur;}return nullptr;}size_t Size(){return _Size(_root);}void InOrder(){_InOrder(_root);}int GETHeight(){return _GETHeight(_root);}bool IsBalance(){return _isBalance(_root);}	
private:bool _isBalance(Node* root){if (root->_bf >= 2 || root->_bf <= -1) return false;int HeightLeft = _GETHeight(root->_left);int HeightRight = _GETHeight(root->_right);if (abs(HeightRight - HeightLeft) > 1) return false;return _isBalance(root->_left) && _isBalance(root->_right);}int _GETHeight(Node* root){if (root == nullptr) return 0;return max(_GETHeight(root->_left), _GETHeight(root->_right)) + 1;}void _InOrder(Node* root){if (root == nullptr) return;_InOrder(root->_left);cout << root->_key << " ";_InOrder(root->_right);}size_t _Size(Node* root){if (root == nullptr) return 0;size_t SizeLeft = _Size(root->_left);size_t SizeRight = _Size(root->_right);return SizeLeft + SizeRight + 1;}//右旋void _RotateR(Node* pParent){Node* LSub = pParent->_left;Node* LSubR = LSub->_right;Node* pPParent = pParent->_parent;if(LSubR)LSubR->_parent = pParent;pParent->_left = LSubR;LSub->_right = pParent;pParent->_parent = LSub;LSub->_parent = pPParent;if (pParent == _root) _root = LSub;else{if (pPParent->_left == pParent) pPParent->_left = LSub;else pPParent->_right = LSub;}//更新平衡因子LSub->_bf = pParent->_bf = 0;}//左旋void _RotateL(Node* pParent){Node* RSub = pParent->_right;Node* RSubL = RSub->_left;Node* pPParent = pParent->_parent;if (RSubL) RSubL->_parent = pParent;pParent->_right = RSubL;RSub->_left = pParent;pParent->_parent = RSub;RSub->_parent = pPParent;if (pParent == _root) _root = RSub;else{if (pPParent->_left == pParent) pPParent->_left = RSub;else pPParent->_right = RSub;}//更新平衡因子RSub->_bf = pParent->_bf = 0;}void _RotateRL(Node* pParent){Node* RSub = pParent->_right;Node* RSubL = RSub->_left;int bf = RSubL->_bf;_RotateR(RSub);_RotateL(pParent);RSub->_bf = 0;if (bf == 1){RSub->_bf = 0;pParent->_bf = -1;}else if (bf == -1){pParent->_bf = 0;RSub->_bf = 1;}else{RSub->_bf = pParent->_bf = 0;}}void _RotateLR(Node* pParent){Node* LSub = pParent->_left;Node* LSubR = LSub->_right;int bf = LSubR->_bf;_RotateL(LSub);_RotateR(pParent);LSub->_bf = 0;if (bf == 0){LSubR->_bf = LSubR->_bf = 0;}else if (bf == 1){LSub->_bf = -1;pParent->_bf = 0;}else if (bf == -1){LSub->_bf = 0;pParent->_bf = 1;}}private:Node* _root = nullptr;
};void test_AVL01()
{int a[] = { 8, 3, 1, 10, 6, 4, 7, 14, 13 };//int a[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14 };AVLTree<int> t1;for (auto e : a){int i = 1;t1.Insert(e);//cout << "Insert:" << e << "->" << t1.IsBalance() << endl;}cout << t1.GETHeight() << endl;t1.InOrder();//cout << t1.IsBalance() << endl;
}void test_AVL02()
{const int N = 10000000;vector<int> v;v.reserve(N);srand(time(0));for (size_t i = 0; i < N; i++){v.push_back(rand() + i);//cout << v.back() << endl;}size_t begin2 = clock();AVLTree<int> t;for (auto e : v){t.Insert(e);//cout << "Insert:" << e << "->" << t.IsBalance() << endl;}size_t end2 = clock();cout << "Insert:" << end2 - begin2 << endl;//cout << t.IsBalance() << endl;cout << "Height:" << t.GETHeight() << endl;cout << "Size:" << t.Size() << endl;size_t begin1 = clock();// 确定在的值for (auto e : v){t.Find(e);}// 随机值/*for (size_t i = 0; i < N; i++){t.Find((rand() + i));}*/size_t end1 = clock();cout << "Find:" << end1 - begin1 << endl;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/37845.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Mac 如何安装 wget

1.安装 Homebrew2.安装 wget3.检测 wget 是否安装成功 1.安装 Homebrew 在安装 wget 之前需要安装一个适用于 mac 的包管理器 Homebrew&#xff0c;打开 mac 终端执行如下命令进行安装&#xff1a; /usr/bin/ruby -e "$(curl -fsSL https://cdn.jsdelivr.net/gh/ineo6/h…

新手第一个漏洞复现:MS17-010(永恒之蓝)

文章目录 漏洞原理漏洞影响范围复现环境复现步骤 漏洞原理 漏洞出现在Windows SMB v1中的内核态函数srv!SrvOs2FeaListToNt在处理FEA&#xff08;File Extended Attributes&#xff09;转换时。该函数在将FEA list转换成NTFEA&#xff08;Windows NT FEA&#xff09;list前&am…

【Golang - 90天从新手到大师】Day14 - 方法和接口

一&#xff0e; go方法 go方法&#xff1a;在函数的func和函数名间增加一个特殊的接收器类型&#xff0c;接收器可以是结构体类型或非结构体类型。接收器可以在方法内部访问。创建一个接收器类型为Type的methodName方法。 func (t Type) methodName(parameter list) {}go引入…

解决“Duplicate keys detected: ‘ ‘.This may cause an update error.”问题

问题原因 出现“Duplicate keys detected”的错误&#xff0c;通常表示在v-for指令中使的:key绑定值有重复。 如果前端是静态数据&#xff0c;一般能自我避免:key绑定值有重复。如果前端是绑定的动态数据&#xff0c;那么需要另外提供一个唯一的键。 在这个例子中&#xff0c…

【LeetCode】接雨水

目录 一、题目二、解法完整代码 一、题目 给定 n 个非负整数表示每个宽度为 1 的柱子的高度图&#xff0c;计算按此排列的柱子&#xff0c;下雨之后能接多少雨水。 示例 1&#xff1a; 输入&#xff1a;height [0,1,0,2,1,0,1,3,2,1,2,1] 输出&#xff1a;6 解释&#xff…

【UML用户指南】-23-对高级行为建模-状态机

目录 1、概述 2、状态 2.1、状态的组成 3、转移 3.1、转移的组成 4、高级状态和转移 4.1、进入效应和退出效应 4.2、内部转移 4.3、do活动 4.4、延迟事件 4.5、子状态机 5、子状态 5.1、非正交子状态 5.2、历史状态 5.3、正交子状态 6、分叉与汇合 7、主动对象…

GOROOT GOPATH GOPROXY GO111MODULE

GOROOT GOROOT代表Go的安装目录。可执行程序go(或go.exe)和gofmt(或gofmt.exe)位于 GOROOT/bin目录中。 配置GOROOT环境变量&#xff0c;其值为Go的安装目录&#xff1b;然后在环境变量PATH中添加GOROOT/bin路径。 注意&#xff1a;GOROOT变量只是代表了安装目录&#xff0c;不…

【面试题】信息系统安全运维要做什么

信息系统安全运维是确保信息系统稳定、可靠、安全运行的一系列活动和措施。 其主要包括以下几个方面&#xff1a; 1.系统监控&#xff1a; 实时监测信息系统的运行状态&#xff0c;如服务器的性能指标、网络流量、应用程序的运行情况等。通过监控工具&#xff0c;及时发现系统…

企业数据治理的下一步是数据资产管理?

随着信息技术的飞速发展和数字化转型的深入推进&#xff0c;企业数据已成为驱动业务增长和创新的核心要素。当企业数据治理工作取得显著成效后&#xff0c;如何进一步发挥数据的价值&#xff0c;实现数据资产的有效管理&#xff0c;成为企业面临的重要课题。 数据治理的基石作用…

学习感悟丨在誉天学习数通HCIP怎么样

大家好&#xff0c;我是誉天学员的徐同学&#xff0c;学习的数通HCIP课程。 在学校的时候&#xff0c;听说下半年就要出去实习了&#xff0c;心中坎坷不安&#xff0c;现在我学到的知识远远不够的。然后就想着学点东西充实一下自己的知识面和专业能力&#xff0c;有一次和同学谈…

【漏洞复现】飞企互联——SQL注入

声明&#xff1a;本文档或演示材料仅供教育和教学目的使用&#xff0c;任何个人或组织使用本文档中的信息进行非法活动&#xff0c;均与本文档的作者或发布者无关。 文章目录 漏洞描述漏洞复现测试工具 漏洞描述 飞企互联-FE企业运营管理平台是一个基于云计算、智能化、大数据…

[图解] 向量数据库之何谓乘积量化器?

Product Quantization 在前面一节讲解了向量数据库索引相关的内容&#xff0c;那么本节将会讲解其中压缩方法的量化手段&#xff1a;乘积量化器。 简单来说将向量的所有维度划分为多个子空间&#xff0c;每个子空间一部分维度&#xff0c;然后每个子空间独立去找最近距离。例如…

haproxy实现代理和负载均衡

HaProxy介绍&#xff1a; haproxy是法国开发者威利塔罗在2000年使用C语言开发的一个开源软件&#xff0c;是一款具备高并发(一万以上)、高性能的TCP和HTTP负载均衡器&#xff0c;支持基于cookie的持久性&#xff0c;自动故障切换&#xff0c;支持正则表达式及web状态统计&…

Numpy array和Pytorch tensor的区别

1.Numpy array和Pytorch tensor的区别 笔记来源&#xff1a; 1.Comparison between Pytorch Tensor and Numpy Array 2.numpy.array 4.Tensors for Neural Networks, Clearly Explained!!! 5.What is a Tensor in Machine Learning? 1.1 Numpy Array Numpy array can only h…

信息学奥赛初赛天天练-39-CSP-J2021基础题-哈夫曼树、哈夫曼编码、贪心算法、满二叉树、完全二叉树、前中后缀表达式转换

PDF文档公众号回复关键字:20240629 2022 CSP-J 选择题 单项选择题&#xff08;共15题&#xff0c;每题2分&#xff0c;共计30分&#xff1a;每题有且仅有一个正确选项&#xff09; 5.对于入栈顺序为a,b,c,d,e的序列&#xff0c;下列( )不合法的出栈序列 A. a&#xff0c;b&a…

螺旋矩阵问题C代码

给定一个n行m列的二维数组&#xff0c;要求按顺时针螺旋顺序输出矩阵中的所有元素&#xff0c;n和m小于等于10 如下图是一个三行四列的螺旋矩阵 要求输出 1 2 3 4 8 12 11 10 9 5 6 7 全局变量定义 int a[11][11]; int vis[11][11]; // 访问标记数组关键代码如下 int dx[] …

MySQL高级-MVCC-基本概念(当前读、快照读)

文章目录 1、MVCC基本概念1.1、当前读1.1.1、创建表 stu1.1.2、测试 1.2、快照读 1、MVCC基本概念 全称Multi-Version Concurrency Control&#xff0c;多版本并发控制。指维护一个数据的多个版本&#xff0c;使得读写操作没有冲突&#xff0c;快照读为MySQL实现MVCC提供了一个…

镂空的文字?分享 1 段优质 CSS 代码片段!

大家好&#xff0c;我是大澈&#xff01; 本文约 800 字&#xff0c;整篇阅读约需 1 分钟。 每日分享一段优质代码片段。 今天分享一段优质 CSS 代码片段&#xff0c;实现 CSS 文字镂空的效果。 老规矩&#xff0c;先阅读代码片段并思考&#xff0c;再看代码解析再思考&#…

Leetcode3190. 使所有元素都可以被 3 整除的最少操作数

Every day a Leetcode 题目来源&#xff1a;3190. 使所有元素都可以被 3 整除的最少操作数 解法1&#xff1a;遍历 遍历数组&#xff0c;累加最少操作数&#xff0c;即 min(num % 3, 3 - num % 3)。 代码&#xff1a; /** lc appleetcode.cn id3190 langcpp** [3190] 使所…

uniapp+vue3开发微信小程序踩坑集

本文主要记录使用uniappvue3开发微信小程序遇见的各种常见问题及注意点。&#xff08;持续更新&#xff09; 问题&#xff1a; 自定义组件为什么有些样式加不上去 给自定义组件增加class的时候&#xff0c;有时候不生效有时候生效&#xff0c;一度让我怀疑自己记忆错乱。后来…