C++ AVL树

39 篇文章 0 订阅

AVL树

平衡二叉搜索树
我们知道当二叉搜索树退化为单支树时,该树就变得不平衡了,所以为了解决此问题,就引出了平衡二叉搜索树-----当向二叉搜索树中插入新结点后,如果能保证每个结点的左右子树高度之差的绝对值不超过1,这样的树称为平衡二叉搜索树。

平衡二叉搜索树有两种:其中一种为AVL树,另一种为红黑树
AVL树
性质:
1.必须是二叉搜索树。
2.左右子树都是AVL树。
3.左右子树高度之差(称为平衡因子)的绝对值不超过1
4.AVL树搜索时,时间复杂度为O(lgN)。

AVL树的操作:
AVL树结点的定义:

struct AVLTreeNode
{
	// T-->int--T()-->int()==0 T如果是内置类型,T()是0
	// T-->Date-T()-->Date()  如果T是自定义类型,该类必须提供无参的构造函数
	AVLTreeNode(const T& data = T())
	: _pLeft(nullptr)
	, _pRight(nullptr)
	, _pParent(nullptr)
	, _data(data)
	, _bf(0)
	{}

	AVLTreeNode<T>* _pLeft;
	AVLTreeNode<T>* _pRight;
	AVLTreeNode<T>* _pParent;
	T _data;
	int _bf;//保存每个结点的平衡因子
};

AVL树的插入

1.如果是空树的话,直接插入即可。
2.如果不是空树,按照二叉搜索树的方式,插入新结点。
3.新结点插入成功后,新结点的父节点parent的平衡因子_bf需要更新。
(规定结点的平衡因子为其右子树高度减去其左子树高度)
插入新的结点,AVL树的平衡性有可能遭到破坏,所以要更新平衡因子,来检查是否破坏了平衡性。
a.如果插入在parent的左侧,只需将parent的平衡因子-1即可。
b.如果插入在parent的右侧,只需将parent的平衡因子+1即可。

此时AVL输的平衡性可能会被破坏。
a.如果插入后parent的平衡因子为0,则说明插入之前parent结点只有左子树(右子树),插入后更新为0,此时平衡性没有破坏,以parent为根的树高度没有增加,不会影响上层,所以无需继续更新。
b.如果插入后parent的平衡因子为1/-1,则说明插入之前parent结点为叶子,插入后更新为1/-1,代表此时以parent为根的树高度增加,虽然没有破坏平衡性,但是会对上层造成影响,所以需要继续向上更新平衡因子。
c.如果插入后parent的平衡因子为2/-2,说明平衡已经被破坏掉了,需要进行旋转处理,将树恢复平衡性。

旋转处理
如果一棵树原本是AVL树,插入一个节点后破坏了树的平衡性,此时就要调整树的结构,失之再次平衡化。根据插入节点位置的不同,分为四种旋转—右单旋、左单旋、左右双旋、右左双旋

1.右单旋
将新节点插入在较高左子树的左侧。
在这里插入图片描述
在这里插入图片描述
实现代码:

Node* pSubL = pParent->_pLeft;
		Node* pSubLR = pSubL->_pRight;

		pParent->_pLeft = pSubLR;
		if (pSubLR)
			pSubLR->_pParent = pParent;

		pSubL->_pRight = pParent;
		Node* pPParent = pParent->_pParent;
		pSubL->_pParent = pPParent;
		pParent->_pParent = pSubL;

		if (nullptr == pPParent)
			_pRoot = pSubL;
		else
		{
			if (pPParent->_pLeft == pParent)
				pPParent->_pLeft = pSubL;
			else
				pPParent->_pRight = pSubL;
		}

		pParent->_bf = pSubL->_bf = 0;

2.左单旋
将新节点插入在较高右子树的左侧左单旋旋转情况可参照右单旋。
实现代码:

Node* pSubR = pParent->_pRight;
		Node* pSubRL = pSubR->_pLeft;

		pParent->_pRight = pSubRL;
		if (pSubRL)
			pSubRL->_pParent = pParent;

		pSubR->_pLeft = pParent;
		Node* pPParent = pParent->_pParent;
		pSubR->_pParent = pPParent;
		pParent->_pParent = pSubR;

		if (nullptr == pPParent)
			_pRoot = pSubR;
		else
		{
			if (pParent == pPParent->_pLeft)
				pPParent->_pLeft = pSubR;
			else
				pPParent->_pRight = pSubR;
		}

		pParent->_bf = pSubR->_bf = 0;

3.左右双旋
将新节点插入较高左子树的右侧,就是先左旋再右旋。
在这里插入图片描述
代码:

		Node* pSubR = pParent->_pRight;
		Node* pSubRL = pSubR->_pLeft;
		int bf = pSubRL->_bf;

		RotateR(pParent->_pRight);
		RotateL(pParent);

		// 需要更新部分节点的平衡因子
		if (bf == 1)
			pParent->_bf = -1;
		else if (bf == -1)
			pSubR->_bf = 1;
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值