【C++】AVL树

AVL树的概念

由于map和set的底层都是二叉树来实现的,但是二叉搜索树有其致命缺陷,就是当插入的元素有序或者接近有序时,就会导致二叉平衡树退化为单支,这样的话,时间复杂度就由O(logN)变成了O(N),就失去了其复杂度的优势,因此需要对退化的这种现象进行调整,来使得二叉平衡树依然平衡。
因此需要在插入节点时保证每个节点的左右子树高度差不超过1,也就是要降低树的高度,来减小搜索的长度。而这也是AVL树的概念。
当然AVL树也可以是空树

AVL树节点的定义

因为当前节点需要存储数据,也需要存储左右孩子节点,并且需要存储自己的双亲节点,因此节点的定义如下:

template<class T>
struct AVLTreeNode 
{
	AVLTreeNode(const T& data = T())
		:_left(nullptr)
		,_right(nullptr)
		,_parent(nullptr)
		,_data(data)
		,_bf(0)
	{}
	AVLTreeNode<T>* _left;
	AVLTreeNode<T>* _right;
	AVLTreeNode<T>* _parent;

	T _data;
	int _bf;	//平衡因子
};

AVL树的插入

AVL树的插入,整个过程也是分两步的,第一步按照二叉搜索树的插入规则进行插入,第二步,插入节点后,AVL树的平衡性可能会遭到破坏,此时就需要进行更新平衡因子,并且检测是否破坏了树的平衡性。
在这里插入图片描述

如图所示,当插入一个新节点后,就需要对其平衡因子进行修改,可以看到,插入节点本身的平衡因子不用修改,而其双亲节点的平衡因子一定会改变的,如果插入的节点是在双亲节点的左子树中,那么其双亲节点的平衡因子就要减减,如果插入到双亲的右子树中,那么就要对其双亲的平衡因子加加。
那么其双亲节点的双亲节点也会由于当前双亲节点的平衡因子变化而收到影响,那么就需要继续向上调整平衡因子。

AVL树的旋转

由于AVL树插入节点后,会破坏当前AVL树的平衡性,那么就需要对其进行操作,以便于当前AVL树重新恢复其平衡性,也就是要让其平衡因子都小于等于1的绝对值。


而我们需要根据节点插入位置的不同,来对AVL树进行分情况旋转操作:
1.新节点插入到较高右子树的外侧(右侧)----->左单旋
2.新节点插入到较高左子树的外侧(左侧)----->右单旋
3.新节点插入到较高右子树的内侧(左侧)----->右左双旋
4.新节点插入到较高左子树的内侧(右侧)----->左右单旋

上述旋转操作中,情况3.右左双旋是先将待旋转的子树进行右旋,变成情况1.然后进行左单旋。情况4.左右双旋是先将待旋转的子树进行左旋,变成情况2.然后进行右单旋。

那么左单旋和右单旋的情况就非常重要了。
下面分别介绍四种情况具体的旋转操作:

1.左单旋

在这里插入图片描述

如图所示:要插入5这个节点时,就属于插入到较高右子树的右侧,那么就需要进行左单旋,具体操作如下:

1.先保存2这个节点的右子树(subR),再保存右子树的左子树(subRL)
2.让2这个节点的右指向subRL(注意,subRL如果不为空,需要让subRL的双亲指向当前2这个节点)
3.让subR的左指向2这个节点
4.再保存2这个节点的双亲节点(pparent)
5.让2这个节点的双亲指向subR
6.让subR的双亲指向pparent
7.如果当前pparent为空,则证明当前subR就是根节点;如果不为空,则判断subR是pparent的左右孩子,对pparent进行赋值即可。
8.最终要更新2这个节点的平衡因子和subR这个节点的平衡因子为0。

2.右单旋

在这里插入图片描述
如图所示:要插入1这个节点时,就属于插入到较高左子树的左侧,那么就需要进行右单旋,具体操作如下:

1.先保存6这个节点的左子树subL和左子树的右子树subLR
2.让6这个节点的左指向subLR
3.让subL的右指向6这个节点
4.保存当前6的双亲节点(pparent)
5.让6这个节点的双亲指向subL
6.让subL的双亲指向pparent
7.判断pparent是否为空,若为空,则subL就是根节点,若不为空,则判断subL是pparent的左右孩子,对pparent进行赋值即可。
8.最终要更新6和subL这两个节点的平衡因子。

3.右左双旋

在这里插入图片描述

如图所示:要插入3这个节点时,就属于插入到较高右子树的左侧,那么就需要进行右左双旋,具体操作如下:

1.保存2这个节点的右子树(subR)和右孩子的左节点(subRL)还有subRL的平衡因子
2.对subR进行右单旋
3.对2这个节点进行左单旋
4.需要进行判断,若subRL的平衡因子是-1,需要设置subR的平衡因子为1,若subRL的平衡因子是1,要对2这个节点的平衡因子设为-1;

4.左右双旋

在这里插入图片描述
如图所示:要插入4这个节点时,就属于插入到较高左子树的右侧,那么就需要进行左右双旋,具体操作如下:

1.保存5这个节点的左(subL)和subL的右(subLR)
2.对subL进行左旋
3.对整个子树进行右旋
4.如果subLR的平衡因子是-1,需要对当前子树的平衡因子设为1;如果subLR的平衡因子是1,需要对subL的平衡因子设为-1

AVL树的性能

AVL树是一棵绝对平衡的二叉搜索树,其要求每个节点的左右子树高度差的绝对值都不超过1,这样可以保证查询时高效的时间复杂度O(log2N);但是如果要对AVL树做一些结构修改的操作,性能非常低下,比如:插入时要维护其绝对平衡,旋转的次数比较多,更差的是在删除时,有可能一直要让旋转持续到根的位置。因此:如果需要一种查询高效且有序的数据结构,而且数据的个数为静态的(即不会改变),可以考虑AVL树,但一个结构经常修改,就不太适合。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值