首先我们需要了解一下AVL树概念:
二叉树虽然可以提高搜索的效率,但是当数据有序接近有序二叉树将退化为单支树,查找元素相当于在顺序表中搜索元素,效率低下,因此便有人提出了AVL树的概念,当向二叉树插入新的节点后,如果可以保证每个节点的左右子树高度之差得到绝对值不超过1,即可降低树的高度,从而减少平均搜索长度
接下来我们就来简易搭建一下构架:
- template<class K,class V>
- struct AVLTreeNode
- {
- AVLTreeNode
*_left; - AVLTreeNode
*_right; - AVLTreeNode
*_parent; - pair
_kv; - int _bf;
- AVLTreeNode(const pair
&kv) - :_left(nullptr)
- ,_right(nullptr)
- ,_parent(nullptr)
- ,_kv(kv)
- ,_bf(0)
- {}
- }
- template<class K,class V>
- {
- typedef AVLTreeNodeM
Node; - public:
- bool Insert(const pair
& kv) - {
- if(_root==nullptr)
- {
- _root=new Node(kv);
- return true;
- }
- Node* parent=nullptr;
- Node* cur=_root;
- while(cur)
- {
- if(cur->_kv.first
- {
- parent=cur;
- cur=cur->_right;
- }
- else if(cur->_kv.first>kv.first)
- {
- parent=cur;
- cur=cur->_left;
- }
- else//二者相同的情况
- {
- return false;
- }
- }
- cur=new Node(kv);
- if(parent->_kv.first
- {
- parent->_right=cur;
- }
- else if(parnet->kv.first>kv.first)
- {
- parent->_left=cur;
- }
- cur->_parent=parent;
- return true;
- }
- private:
- Node* _root=nullptr;
- }
这里我们bf设置为0即可,接下来我么就来对平衡因子进行编写:
更新平衡因子的规则:
(1)新增在右,parent->bf++;新增在左,parent->bf--;
(2)更新后,parent->bf==1 or -1,说明parent之前是0,(注意:在这里parent之前一定不是2或者-2,否则之前就是不平衡的),说明左右子树高度相同,插入后有一边高,parent的高度变了,需要继续往上更新
(3)更新后,parent->bf==0,说明此时就不需要继续往上更新
(4)如果更新后,parent->bt==2或者-2,说明说明原来就是1或者-1,已经是平衡的临界值
- template<class K,class V>
- struct AVLTreeNode
- {
- AVLTreeNode
*_left; - AVLTreeNode
*_right; - AVLTreeNode
*_parent; - pair
_kv; - int _bf;
- AVLTreeNode(const pair
&kv) - :_left(nullptr)
- ,_right(nullptr)
- ,_parent(nullptr)
- ,_kv(kv)
- ,_bf(0)
- {}
- }
- template<class K,class V>
- {
- typedef AVLTreeNodeM
Node; - public:
- bool Insert(const pair
& kv) - {
- if(_root==nullptr)
- {
- _root=new Node(kv);
- return true;
- }
- Node* parent=nullptr;
- Node* cur=_root;
- while(cur)
- {
- if(cur->_kv.first
- {
- parent=cur;
- cur=cur->_right;
- }
- else if(cur->_kv.first>kv.first)
- {
- parent=cur;
- cur=cur->_left;
- }
- else//二者相同的情况
- {
- return false;
- }
- }
- cur=new Node(kv);
- if(parent->_kv.first
- {
- parent->_right=cur;
- }
- else if(parnet->kv.first>kv.first)
- {
- parent->_left=cur;
- }
- cur->_parent=parent;
- //平衡因子编写
- while(parent)
- {
- if(cut==parent->_right)
- {
- parent->_bf++;
- }
- else
- {
- parent->_bf--;
- }
- if(parent->_bf==0)
- {
- break;
- }
- if(abs(parent>_bf)==1)
- {
- parent=parent->_parent;
- cur=cur->_parent;
- }
- else if(abs(parent->_bf)==2)
- {
- //说明parent的子树已经不平衡了,需要旋转处理
- }
- else
- {
- assert(false);//理论上是不会到这里的
- }
- }
- return true;
- }
- private:
- Node* _root=nullptr;
- }
接下来我们一起来了解一下旋转机制:
首先原则就是:
(1)旋转为平衡树
(2)保持搜索树原则
接下来我们就逐个讲解一下各个旋转规则:、
一.左单旋:
首先我们要知道为什么要叫做左旋转,其实根本就是右边高了,导致平衡因子变大,具体图如下:

最右边的子树加1,此时60的平衡因子就变作1,30就变作2,此时就需要旋转。具体调节方法如上图;
其实也就是只要最右边加成员,就会发生左旋:
一.左旋
- template<class K,class V>
- struct AVLTreeNode
- {
- AVLTreeNode
*_left; - AVLTreeNode
*_right; - AVLTreeNode
*_parent; - pair
_kv; - int _bf;
- AVLTreeNode(const pair
&kv) - :_left(nullptr)
- ,_right(nullptr)
- ,_parent(nullptr)
- ,_kv(kv)
- ,_bf(0)
- {}
- }
- template<class K,class V>
- {
- typedef AVLTreeNodeM
Node; - public:
- bool Insert(const pair
& kv) - {
- if(_root==nullptr)
- {
- _root=new Node(kv);
- return true;
- }
- Node* parent=nullptr;
- Node* cur=_root;
- while(cur)
- {
- if(cur->_kv.first
- {
- parent=cur;
- cur=cur->_right;
- }
- else if(cur->_kv.first>kv.first)
- {
- parent=cur;
- cur=cur->_left;
- }
- else//二者相同的情况
- {
- return false;
- }
- }
- cur=new Node(kv);
- if(parent->_kv.first
- {
- parent->_right=cur;
- }
- else if(parnet->kv.first>kv.first)
- {
- parent->_left=cur;
- }
- cur->_parent=parent;
- //平衡因子编写
- while(parent)
- {
- if(cut==parent->_right)
- {
- parent->_bf++;
- }
- else
- {
- parent->_bf--;
- }
- if(parent->_bf==0)
- {
- break;
- }
- if(abs(parent>_bf)==1)
- {
- parent=parent->_parent;
- cur=cur->_parent;
- }
- else if(abs(parent->_bf)==2)
- {
- //说明parent的子树已经不平衡了,需要旋转处理
- if(parent->_bf==2 &&cur->bf==0)
- {
- Rotatel(parent);
- }
- break;
- }
- else
- {
- assert(false);//理论上是不会到这里的
- }
- }
- return true;
- }
- private:
- void Rotatel(Node* parent)
- {
- Node* subR=parent->_right;
- Node* suRL=subR->_left;
- parent->_right=subRL;
- if(subPL)
- {
- subRL->_parent=parent;
- }
- Node* ppNode=parent->_parent;
- subR->_left=parent;
- parent->_parent=subR;
- if(parent==_root)//如果父母就是根
- {
- _root=subR;
- subR->_parent=nullptr;
- }
- else//如果父母不是根,而是子树
- {
- if(ppNode->_left=parent)
- {
- ppNode->_left=subR;
- }
- else
- {
- ppNode->_right=subR;
- }
- subR->_parent=ppNode;
- }
- subR->_bf=parent->_bf=0;
- }
- private:
- Node* _root=nullptr;
- }
二.右旋
具体图如下:

- void RotateR(Node* parent)
- {
- Node* subL=parent->_left;
- Node* subLR=subL->_right;
- parent->_left=subLR;
- if(subLR)
- {
- subLR->_parent=parent;
- }
- Node* ppNode=parent->_parent;
- subL->_right=parent;
- parent->_parent=subL;
- if(_root==parent)
- {
- _root=subL;
- subL->_parent=nullptr;
- }
- else
- {
- if(ppNode->_left==_parent)
- {
- ppNode->_left=subL;
- }
- else
- {
- ppNode->_right=subL;
- }
- subL->_parent=ppNode;
- }
- subL->_bf=parent->_bf=0;
- }
前提条件如下:
- if((parent->_bf==-2) && (cur->_bf==-1))
- {
- RotateR(parent);
- }
三.双旋
(1)左右双旋
- void RotateLR(Node* parent)
- {
- Node* subL=parent->_left;
- Node* subLR=subL->_right;
- int bf=subLR->_bf;
- RotateL(parent->_left);
- RotateR(parent);
- subLR->_bf=0;
- if(bf==1)
- {
- parent->_bf=0;
- subL->_bf=-1;
- }
- else if(bf==-1)
- {
- parent->_bf=1;
- subL->_bf=0;
- }
- else if(bf==0)
- {
- parent->_bf=0;
- subL->_bf=0;
- }
- else
- {
- assert(false);
- }
- }
(2)右左双旋
- void RotateRL(Node* parent)
- {
- Node* subR=parent->_right;
- Node* subRL=subR->_left;
- int bf=subRL->_bf;
- RotateR(parent->_left);
- RotateL(parent);
- if(bf==1)
- {
- subR->_bf=0;
- parent->_bf=-1;
- subRL->_bf=0;
- }
- else if(bf==-1)
- {
- subR->_bf=1;
- parent->_bf=0;
- subRL->_bf=0;
- }
- else if(bf==0)
- {
- parent->_bf=0;
- subR->_bf=0;
- }
- else
- {
- assert(false);
- }
- }
最后我们来了解一下旋转的价值与意义:
1.使当前这棵树平衡
2.降低树的高度
以上就是文章所有内容,希望对您有帮助!
-
相关阅读:
深度学习的数值问题
使用c#的 async/await编写 长时间运行的基于代码的工作流的 持久任务框架
华为手机的10个使用技巧,你知道吗
并查集的实现的应用
java基于ssm+vue的考研信息查询系统 elementui
如何安装TortoiseSVN并实现公网提交文件至本地SVN服务器?
java数据库linux面试(详细)
Kali Linux渗透测试高级篇 1-1 OSINT介绍
Python 学习 第二册 第14章 网络编程
漫谈:C语言 C++ 迷惑的语句、分号、大括号
-
原文地址:https://blog.csdn.net/weixin_64394633/article/details/127694766