AVL树

AVL树
今天我想和大家分享一下AVL树的结构和原理。
两位俄罗斯的数学家Adelson-Velskii和Landis在1962年发明了AVL树,因此将他们的名字结合起来就是AVL树的名字。
(一)
AVL树的的规则:
一棵AVL树是包括空树,或者具有以下性质的二叉搜索树。
1.树的左右子树都是AVL树。
2.左右子树高度之差(简称平衡因子)的绝对值不超过(-1/0/1)。

  平衡因子=右子树的高度-左子树的高度

平衡因子不是必须的,它只是一种控制方式,帮助我们去更便捷的控制一棵树。
在这里插入图片描述
如图所示:
先看左下角,叶子节点2的左右子树高度均为0,所以他的平衡因子为0,结点3的右子树高度为0,左子树高度为1,所以平衡因子为-1,接着看5这个结点,它的左右子树均高度为0,所以平衡因子为0,再看根结点,它的左子树高度为2,右子树高度为1,所以平衡因子为-1,这下大家对平衡因子就有了解了。
(二)
我要带着大家一块分析AVL树的插入,AVL树的插入就跟搜索树一样,比根小的往左子树去插入,比根大的往右子树去插入。插入完成后需要更新平衡因子,才能算一次插入的完成。
在这里插入图片描述
我们把当前新插入的孩子结点叫做cur,这个子树的根结点叫做parent,再把父亲的父亲结点叫做grandfather。
插入新结点可能会影响新增结点的部分祖先,也就是根节点8的右子树。这里我们把平衡因子写成bf(balance factor),是否要更新平衡因子取决于插入cur结点后parent的高度差是否会发生变化,是否会影响grandfather。
这里我分析三种可能:
一.
更新后,p->bf== 0,p所在的子树高度不变,说明更新前p的平衡因子是1或者-1,p的矮的那边插入了cur结点,p的左右均很了,bf为0,不会影响爷爷结点。
二.
更新后,p->bf==-1或者p->bf== 1,所在子树的高度变了,说明更新前p的bf为0,现在p的左右变得不均衡,但不违反左右高度差绝对值为1的规则,但会影响爷爷结点。因此要往上进行bf的更新。
三.
更新后,p->bf==-2或者p->bf==2,p所在的子树违法了AVL树的规则,需要进行旋转操作。旋转接下来会重点讲。
在这里插入图片描述
如图,当cur是10的结点插入后,就违反高度差的规则,就需要进行旋转操作。但旋转后,依然保持着搜索树的规则,左边的小于根结点,右边的大于根结点,此时旋转后符合规则,旋转成功。

这里我将所有的AVL类型归类为一个抽象图。

这里我分为几种情况去讨论旋转类型
一.左旋
在这里插入图片描述
在这里插入图片描述
c的高度发生变化左子树高度为h右子树高度h+2所以左右子树的高度差发生变化,需要旋转。
在这里插入图片描述
二.右旋
在这里插入图片描述

b的高度发生变化左子树高度为h+2右子树高度h所以左右子树的高度差发生变化,需要旋转。
在这里插入图片描述

三.左右双旋
在这里插入图片描述

这里插入c情况比较特殊,往左旋和右旋都不太合适,都不能成功旋转。因此我们在这里选择拆分,我新增一个图,将上图的c看成下图的60这个结点加上b和c字母有重复上图的c和下图的c不是一个,大家辨别清楚。这里我新弄一个图
在这里插入图片描述
这种类型又分为两种旋转类型,左右旋转和右左旋转
左右双旋:
在这里插入图片描述

我们来分析一下,这里中间那条路径新增一个结点,高度由h+1变为h+2,最右边那个结点高度为h,由AVL规则可知,需要发生旋转,这里由于插入的结点在中间,需要2次旋转,由图可知,经过2次旋转,AVL树达到平衡。
右左双旋:
在这里插入图片描述

这里最左边的高度为h,在c处新增一个结点,因此在90这个结点开始数发现这条路径的高度为h+2,因此按照AVL的规则,发生旋转,经过2次旋转,达到平衡。
(三)
我会展示部分旋转的代码,

//这里是AVL树结构的定义
template<class K,class V>
struct AVLTreeNode {
	AVLTreeNode* _left;
	AVLTreeNode* _right;
	AVLTreeNode* _parent;
	int _bf;
	pair<K,V> _kv;
	AVLTreeNode(const pair<K, V>& kv)
		:_left(nullptr)
		,_right(nullptr)
		,_parent(nullptr)
		,_bf(0)
		,_kv(kv)
	{}
};

//左旋转
    void RotateL(Node* parent)
	{
		Node* subR = parent->_right;
		Node* subRL = subR->_left;
		parent->_right = subRL;
		if (subRL)
		{
			subRL->_parent = parent;
		}
		subR->_left = parent;
		Node* ppnode = parent->_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;
		}
		parent->_bf = 0;
		subR->_bf = 0;
	}
//右旋转
	void RotateR(Node* parent)
	{
		Node* subL = parent->_left;
		Node* subLR = subL->_right;
		parent->_left = subLR;
		if (subLR)
		{
			subLR->_parent = parent;
		}
		subL->_right = parent;
		Node* pnode = parent->_parent;
		parent->_parent = subL;
		if (parent == _root)
		{
			_root=subL;
			subL->_parent = nullptr;
		}
		else
		{
			if (pnode->_left == parent)
			{
				pnode->_left = subL;

			}
			else
			{
				pnode->_right = subL;
			}
			subL->_parent = pnode;
		}
			subL->_bf = 0;
			parent->_bf = 0;
	}
//左右
void RotateLR(Node* parent)
	{
		Node* subL = parent->_left;
		Node* subLR = subL->_right;
		int bf=subLR->_bf ;
		RotateL(subL);
		RotateR(parent);
		subLR->_bf = 0;
		if (bf == 0)
		{
			subL->_bf = 0;
			parent->_bf =0;
		}
		else if (bf == 1)
		{
			subL->_bf = -1;
			parent->_bf = 0;
		}
		else if(bf==-1)
		{
			subL->_bf = 0;
			parent->_bf = 1;
		}
		else
		{
			assert(false);
		}
	}
//右左
	void RotateRL(Node* parent)
	{
		Node* subR = parent->_right;
		Node* subRL = subR->_left;
		int bf = subRL->_bf;
		RotateR(subR);
		RotateL(parent);
		subRL->_bf = 0;
		if (bf == 0)
		{
			subR->_bf = 0;
			parent->_bf = 0;
		}
		else if (bf == 1)
		{
			parent->_bf = -1;
			subR->_bf = 0;
		}
		else if (bf == -1)
		{
			parent->_bf = 0;
			subR->_bf = 1;
		}
	}
                       代码已经公开, 制作不易,希望对大家有所帮助!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值