目录
测试一下
二叉搜索树如果不平衡的话,就没办法发挥出二叉搜索树的优势
AVLTree,红黑树
二叉搜索树虽可以缩短查找的效率,但如果数据有序或接近有序二叉搜索树将退化为单支树,查找元素相当于在顺序表中搜索元素,效率低下。因此,两位俄罗斯的数学家G.M.Adelson-Velskii和E.M.Landis在1962年发明了一种解决上述问题的方法:当向二叉搜索树中插入新结点后,如果能保证每个结点的左右子树高度之差的绝对值不超过1
(需要对树中的结点进行调整),即可降低树的高度,从而减少平均搜索长度。
一棵AVL树或者是空树,或者是具有以下性质的二叉搜索树:
它的左右子树都是AVL树
左右子树高度之差(简称平衡因子)的绝对值不超过1(-1/0/1 )
AVL树又可以称为高度平衡二叉搜索树
为什么这里控制高度之差不超过1,而不是相等?
因为没有办法保证。
比方说我们的树只有两个结点,那么必然是一个为根节点,一个为左子树结点或者右子树结点,所以左子树的高度和右子树的高度是不可能相等的。我们做不到
这样的树不是AVL树,因为我们没有满足每一个结点的左右子树的高度的差都是小于等于1
通过控制树的高度去控制树的平衡
这里的平衡因子就是高度差
平衡因子=右子树的高度-左子树的高度
(非必须)也可以不要,我们用的话是为了方便实现。
也就是为了让我们有一个量化的值可以去查看

如果我们是在8的左子树插入了一个7,那么8的左右子树的高度差变成0,依旧满足我们平衡二叉树的特性。
但是如果在9的右子树加上了一个10呢?
此时我们的9的左右子树的差是1,我们8的左右子树的差变成了2,我们需要对其进行调整和旋转。
1、新增在右子树
如果新增的结点在父节点的右子树,我们需要将父节点的平衡因子+1,因为平衡因子的计算时右子树的高度减去左子树的高度,我们这里右子树的高度+1,但是左子树的高度并没有发生变化
parent->bf++
2、新增在左子树
如果新增的结点在父节点的左子树,我们需要将父节点的平衡因子-1,因为平衡因子的计算时右子树的高度减去左子树的高度,我们这里左子树的高度+1,但是右子树的高度并没有发生变化
parent->bf--
3、更新后,parent->bf==1 或者 -1
说明parent更新前的平衡因子是0,说明左右子树高度相等。
插入后有一边高。
parent高度变了,需要继续往上更新!(对上一层有影响)
4、更新后,parent->bf==0
说明parent更新前的平衡因子是1或者-1,说明左右子树高度相差1。
插入后左右子树高度相等,插入填上了矮的那一边。
parent所在的子树高度不变,不需要继续往上更新!(对上一层没有影响)
5、更新后,parent->bf==2 或者 -2
说明parent更新前的平衡因子是1或者-1,说明左右子树高度相差1。
(原本就是平衡的临界值了)
插入后原本就是高的一边更加高,打破了平衡。
parent高度变了,需要旋转处理!
6、更新后parent->bf >2or<-2的值?
不可能,如果存在,则说明插入前就不是AVL树,需要去检查之前的操作的问题。




下面的情况就需要旋转

如何用代码实现我们上述的方法
- //最坏的情况是更新到我们的根节点
- //如果parent为空,也就是更新到了我们的根节点,没有上层节点了
- while(parent)
- {
- if(cur==parent->_right)
- {
- parent->_bf++;
- }
- else
- {
- parent->_bf--;
- }
-
- //父节点的bf为0的话,就停止迭代。
- if((parent->_bf)==0)
- {
- break;
- }
- //如果parent的_bf是1或者-1
- //往上迭代
- else if(abs(parent->_bf)==1)
- {
- parent=parent->_parent;
- cur=cur->_parent;
- }
- else if(abs(parent->_bf)==2)
- {
- //说明parent所在的子树已经不平衡了,需要旋转来调整
- }
- //说明parent的平衡因子>2,也就是在插入我们这个结点前面,我们的平衡二叉树已经不平衡了
- else
- {
- //直接抛出异常
- assert(false);
- }
- }
我们观察到平衡因子是控制我们这一整棵树的关键。
出问题的时候一定是以parent为根节点所在的子树出问题了
处理的原则
1、旋转成平衡树
2、保持搜索树规则
假设我们往树中依次插入
1 3 5

上面的树右边高,我们就需要往左边转,也就是左旋!

更加复杂一点的情况



我们往树中依次插入
5 3 1

旋转的位置有可能是整棵树,也有可能是子树

1.新节点插入较高左子树的左侧---左左:右单旋

2.新节点插入较高右子树的右侧---右右:左单旋

3.新节点插入较高左子树的右侧---左右:先左单旋再右单旋

4.新节点插入较高右子树的左侧---右左:先右单旋再左单旋

按照上面的图例演示,举几个简单的例子
h==0


h==1



所以这里的具象图有着无限多种情况,但是我们用上面的抽象图其实都是可以解释的。
只有parent和subR的平衡因子发生了变化,且都变成0
- void RotateL(Node* parent)
- {
- Node* subR=parent->_right;
- Node* subRL=subR->_left;
-
- parent->_right=subRL;
- //修改三叉链里面的父亲结点
- if(subRL)
- {
- subRL->_parent=parent;
- }
- Node* ppNode=parent->_parent;
-
- subR->_left=parent;
- parent->_parent=subR;
- //1、parent是整棵子树的根
- if(parent==_root)
- {
- _root=subR;
- subR->_parent= nullptr;
- }
- //2、parent是子树的根,也就是parent结点上面还有祖先结点
- else
- {
- if(ppNode->_left==parent)
- {
- ppNode->_left=subR;
- }
- else
- {
- ppNode->_right=subR;
- }
- subR->_parent=ppNode;
- }
- //修改平衡因子
- subR->_bf=parent->_bf=0;
- }

- //右旋
- 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;
- //1.parent是整个子树的根
- if(parent==_root)
- {
- _root=subL;
- subL->_parent= nullptr;
- }
- //2.parent是子树的根,也就是parent结点上面还有祖先结点
- else
- {
- if(ppNode->_left==parent)
- {
- ppNode->_left=subL;
- }
- else
- {
- ppNode->_right=subL;
- }
- subL->_parent=ppNode;
- }
- //修改平衡因子
- subL->_bf=parent->_bf=0;
- }
case1:在b插入

无论是在b的位置插入还是在c的位置插入,b or c高度变为h,都会引发双旋。
case2:在c插入

case3:h=0的情况(60自己就是新增)

这里对应的90的结点就是我们的parent
几个相关结点的标记如下
case1:
case2:
case3:
- //左右旋
- void RotateLR(Node* parent)
- {
- Node* subL=parent->_left;
- Node* subLR=subL->_right;
- int bf=subLR->_bf;
-
- RotateL(parent->_left);
- RotateR(parent);
-
- //平衡因子的更新
- //对上面的三种情况分别更改平衡因子
- subLR->_bf=0;
- //case1
- if(bf==1)
- {
- parent->_bf=0;
- subL->_bf=-1;
- }
- //case2
- else if(bf==-1)
- {
- parent->_bf=1;
- subL->_bf=0;
- }
- //case3
- else if(bf==0)
- {
- parent->_bf=0;
- subL->_bf=0;
- }
- else
- {
- assert(false);
- }
- }
case1:在c位置插入新数据

case2:在b位置插入新数据

case3:h==0,(60自己就是新增)

几个相关结点标记如下
case1:
case2:
case3:
- //左右旋
- void RotateRL(Node* parent)
- {
- Node* subR=parent->_right;
- Node* subRL=subR->_left;
- int bf=subRL->_bf;
-
- RotateR(parent->_right);
- RotateL(parent);
-
- //平衡因子的更新
- //对上面的三种情况分别更改平衡因子
- subRL->_bf=0;
- //case1
- if(bf==1)
- {
- parent->_bf=-1;
- subR->_bf=0;
- }
- //case2
- else if(bf==-1)
- {
- parent->_bf=0;
- subR->_bf=1;
- }
- //case3
- else if(bf==0)
- {
- parent->_bf=0;
- subR->_bf=0;
- }
- else
- {
- assert(false);
- }
- }
旋转的价值和意义
1.让子树保持平衡
2.降低树的高度(高度恢复到插入之前的样子)
-
- #ifndef AVL_AVLTREE_H
- #define AVL_AVLTREE_H
- #pragma once
- using namespace std;
- template<class K,class V>
- struct AVLTreeNode
- {
- AVLTreeNode
* _left; - AVLTreeNode
* _right; - //三叉链,更新结点可能会影响结点的祖先
- AVLTreeNode
* _parent; -
- pair
_kv; - int _bf;//平衡因子 balance factor
-
- AVLTreeNode(const pair
&kv) - :_left(nullptr)
- ,_right(nullptr)
- ,_parent(nullptr)
- ,_kv(kv)
- ,_bf(0)
- {
-
- }
- };
-
- template<class K,class V>
- struct AVLTree
- {
- typedef AVLTreeNode
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
- {
- parent->_left=cur;
- }
-
- //及时更新我们三叉链的parent的属性
- cur->_parent=parent;
-
- //控制平衡
- //1、沿着这个位置往上更新平衡因子
- //这里的新插入的结点会影响祖先结点的平衡
-
- //最坏的情况是更新到我们的根节点
- //如果parent为空,也就是更新到了我们的根节点,没有上层节点了
- while(parent)
- {
- if(cur==parent->_right)
- {
- parent->_bf++;
- }
- else
- {
- parent->_bf--;
- }
-
- //父节点的bf为0的话,就停止迭代。
- if((parent->_bf)==0)
- {
- break;
- }
- //如果parent的_bf是1或者-1
- //往上迭代
- else if(abs(parent->_bf)==1)
- {
- parent=parent->_parent;
- cur=cur->_parent;
- }
- else if(abs(parent->_bf)==2)
- {
- //说明parent所在的子树已经不平衡了,需要旋转来调整
-
- //右右--左旋
- 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)
- {
- RotateLR(parent);
- }else if(parent->_bf==2&&cur->_bf==-1)
- {
- RotateRL(parent);
- }else{
- assert(false);
- }
- break;
- }
- //说明parent的平衡因子>2,也就是在插入我们这个结点前面,我们的平衡二叉树已经不平衡了
- else
- {
- //直接抛出异常
- assert(false);
- }
- }
- return true;
- }
-
- void InOrder()
- {
- _InOrder(_root);
- cout << endl;
- }
-
- private:
- //左旋
- void RotateL(Node* parent)
- {
- Node* subR=parent->_right;
- Node* subRL=subR->_left;
-
- parent->_right=subRL;
- //修改三叉链里面的父亲结点
- if(subRL)
- {
- subRL->_parent=parent;
- }
- Node* ppNode=parent->_parent;
-
- subR->_left=parent;
- parent->_parent=subR;
- //1、parent是整棵子树的根
- if(parent==_root)
- {
- _root=subR;
- subR->_parent= nullptr;
- }
- //2、parent是子树的根,也就是parent结点上面还有祖先结点
- else
- {
- if(ppNode->_left==parent)
- {
- ppNode->_left=subR;
- }
- else
- {
- ppNode->_right=subR;
- }
- subR->_parent=ppNode;
- }
- //修改平衡因子
- subR->_bf=parent->_bf=0;
- }
-
- //右旋
- 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;
- //1.parent是整个子树的根
- if(parent==_root)
- {
- _root=subL;
- subL->_parent= nullptr;
- }
- //2.parent是子树的根,也就是parent结点上面还有祖先结点
- else
- {
- if(ppNode->_left==parent)
- {
- ppNode->_left=subL;
- }
- else
- {
- ppNode->_right=subL;
- }
- subL->_parent=ppNode;
- }
- //修改平衡因子
- subL->_bf=parent->_bf=0;
- }
-
- //左右旋
- void RotateLR(Node* parent)
- {
- Node* subL=parent->_left;
- Node* subLR=subL->_right;
- int bf=subLR->_bf;
-
- RotateL(parent->_left);
- RotateR(parent);
-
- //平衡因子的更新
- //对上面的三种情况分别更改平衡因子
- subLR->_bf=0;
- //case1
- if(bf==1)
- {
- parent->_bf=0;
- subL->_bf=-1;
- }
- //case2
- else if(bf==-1)
- {
- parent->_bf=1;
- subL->_bf=0;
- }
- //case3
- else if(bf==0)
- {
- parent->_bf=0;
- subL->_bf=0;
- }
- else
- {
- assert(false);
- }
- }
-
- //左右旋
- void RotateRL(Node* parent)
- {
- Node* subR=parent->_right;
- Node* subRL=subR->_left;
- int bf=subRL->_bf;
-
- RotateR(parent->_right);
- RotateL(parent);
-
- //平衡因子的更新
- //对上面的三种情况分别更改平衡因子
- subRL->_bf=0;
- //case1
- if(bf==1)
- {
- parent->_bf=-1;
- subR->_bf=0;
- }
- //case2
- else if(bf==-1)
- {
- parent->_bf=0;
- subR->_bf=1;
- }
- //case3
- else if(bf==0)
- {
- parent->_bf=0;
- subR->_bf=0;
- }
- else
- {
- assert(false);
- }
- }
- void _InOrder(Node* root)
- {
- if (root == nullptr)
- {
- return;
- }
-
- _InOrder(root->_left);
- cout << root->_kv.first << ":" << root->_kv.second << endl;
- _InOrder(root->_right);
- }
-
-
- private:
- Node* _root=nullptr;
-
- };
-
-
- #endif //AVL_AVLTREE_H
- void TestAVLTree1()
- {
- int a[] = { 16, 3, 7, 11, 9, 26, 18, 14, 15 };
- AVLTree<int, int> t1;
- for (auto e : a)
- {
- t1.Insert(make_pair(e, e));
- }
-
- t1.InOrder();
- }

检查是不是平衡二叉树
但是中序遍历有序,只能说明我们的avl树是一棵搜索树,并不能直接说明是平衡二叉树,我们也不能靠检查平衡因子的方式去检查,因为我们代码运行的条件就是检查的平衡因子。所以不能这么查。我们这里不妨查平衡二叉树的高度来检查是不是平衡二叉树
求高度
- 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 leftHT = Height(root->_left);
- int rightHT = Height(root->_right);
- int diff = rightHT - leftHT;
-
- if (diff != root->_bf)
- {
- cout << root->_kv.first << "平衡因子异常" << endl;
- return false;
- }
-
- return abs(diff) < 2
- && _IsBalance(root->_left)
- && _IsBalance(root->_right);
- }
测试
- void TestAVLTree1()
- {
- int a[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14 }; // 测试双旋平衡因子调节
- // int a[] = { 16, 3, 7, 11, 9, 26, 18, 14, 15 };
- AVLTree<int, int> t1;
- for (auto e : a)
- {
- t1.Insert(make_pair(e, e));
- }
-
- t1.InOrder();
- cout <<"IsBalance:"<
IsBalance() << endl; - }


随机插入一组数据测试
- void TestAVLTree2()
- {
- size_t N = 10000;
- srand(time(0));
- AVLTree<int, int> t1;
- for (size_t i = 0; i < N; ++i)
- {
- int x = rand();
- t1.Insert(make_pair(x, i));
- /* bool ret = t1.IsBalance();
- if (ret == false)
- {
- int u = 1;
- }
- else
- {
- cout << "Insert:" << x << " IsBalance:" <
- }*/
- }
- cout << "IsBalance:" << t1.IsBalance() << endl;
- }

-
相关阅读:
Bert相关面试题整理
Dubbo—— 一个服务既是消费者又是提供者
【Spring面试】八、事务相关
积分商城游戏能够给商家带来什么?怎么搭建积分商城?
CentOS 环境 SIPp 安装及使用
b2b2c o2o 多商家入驻商城 直播带货商城 电子商务
攻防世界_MISC之碎纸机11
Photoshop图层混合模式公式(Unity,CG实现)
WinHex(三)
javaweb没有web.xml设置方法
-
原文地址:https://blog.csdn.net/weixin_62684026/article/details/127585338