AVL树的学习

1.1 AVL树的概念

二叉搜索树虽可以缩短查找的效率,但如果数据有序或接近有序二叉搜索树将退化为单支树,查
找元素相当于在顺序表中搜索元素,效率低下。因此,两位俄罗斯的数学家G.M.Adelson-Velskii
和E.M.Landis在1962年
发明了一种解决上述问题的方法:当向二叉搜索树中插入新结点后,如果能保证每个结点的左右
子树高度之差的绝对值不超过1(需要对树中的结点进行调整),即可降低树的高度,从而减少平均
搜索长度。
一棵AVL树或者是空树,或者是具有以下性质的二叉搜索树:
它的左右子树都是AVL树
左右子树高度之差(简称平衡因子)的绝对值不超过1(-1/0/1),记住平衡因子的概念
1.2 AVL树的定义:
template<class K,class V>
struct  AVLTreeNode
{
	pair<K, V> _kv;//存储的数值
	AVLTreeNode<K,V>* left;
	AVLTreeNode<K, V>* right;
	AVLTreeNode<K, V>* parent;//此节点的父亲结点
	int _bf;//平衡因子
};

当然我们也可以不把数值存成pair的形式,这样是和map一致,各有各的用处

AVLTreeNode(const pair<K, V>& kv)
	:_kv(kv)
	,left(nullptr)
	,right(nullptr)
	,parent(nullptr)
	,_bf(0)
{

}

我们用初始化列表的方式完成构造函数,也不算完成吧,因为这个是需要参数的构造函数,这个应该叫做赋值拷贝。

1.3 AVL树的插入

主要分为两步:1. 按照二叉搜索树的方式插入新节点 2. 调整节点的平衡因子

看着简单实则不太容易。

(此截图来着bili的up主:蓝不过海呀) 

主要就是用来失衡怎么做

首先是右单旋,也就是出现此情况的时候,我们要进行右旋,,也就是形成该样子,把平衡因子为2的14结点向下转,也就是右旋,转成六结点的右孩子,然后6的右孩子,就成了14的左孩子,6的右孩子是空也无妨。

void RotateR(Node* parent)
{
	Node* subL = parent->left;
	Node* subLR = subL->right;

	parent->left = subLR;
	if (subLR)
		subLR->parent = parent;

	Node* PParent = parent->parent;
	subL->right = parent;
	parent->parent = subL;
	if (PParent == nullptr)
	{
		root = subL;
		
	}
	else
	{
		if (PParent->right == parent)
			PParent->right = subL;
		else
			PParent->left = subL;
	}
	subL->parent = PParent;
	subL->_bf = parent->_bf = 0;
}

我们把失衡的结点也就是图中的14,传过来,传给parent。由于我们刚才分析过了,会改变6和6的右孩子,所以我们记录此时这几个结点,然后进行改变,最后6顶替了14的位置,如果原来14有父亲的话,要让6成为原来14父亲的孩子,进行重新链接

左旋就是同样的道理

void RotateL(Node* parent)
{
	Node* subR = parent->right;
	Node* subRL = subR->left;

	parent->right = subRL;
	if (subRL)
		subRL->parent = parent;

	Node* PParent = parent->parent;
	subR->left = parent;
	parent->parent = subR;

	if (PParent)
	{
		subR->parent = PParent;
		if (PParent->left == parent)
			PParent->left = subR;
		else
			PParent->right = subR;
	}
	else
	{
		subR->parent = nullptr;
		root = subR;
	}
	//平衡因子
	subR->_bf = parent->_bf = 0;
		
}

 我们再代码区域看到了,我已经进行了平衡因子的更新,接下来我们说一下单旋的平衡因子是怎么更新的。我们以左单旋为例子。

,不要纠结为什么是h啥的高度,只是为了更好的计算平衡因子,相当于数学的代入字母证明的一般式,所以我们可以得到,如果单选的话,parent和parent->left或者right 的平衡因子都更新成0。

接着我们来解决另外两种双旋的,举例子我们用先左旋再右旋的,也就是LR型

先进行第一次旋转,这是一次左旋,接上的话,,就又成为了应该刚才右旋的,因为此时的平衡因子是-2 和-2.总结一下,就是失衡的结点再次成为parent ,然后parent的left要往下转,也就是左旋,然后成为它右孩子的子结点,也就是成为parent->left->right的子结点,正好对应了LR型,然后进行右旋,使得parent->left->right成为新的此时的“根”结点。

void RotateLR(Node* parent)
{
	Node* subL = parent->left;
	Node* subLR = subL->right;
	int bf = subLR->_bf;
	RotateL(parent->left);
	RotateR(parent);
	
	if (bf == 0)
	{
		subL->_bf = 0;
		subLR->_bf = 0;
		parent->_bf = 0;
	}
	else if (bf == 1)
	{
		subLR->_bf = 0;
		subL->_bf = -1;
		parent->_bf = 0;
	}
	else if (bf == -1)
	{
		subLR->_bf = 0;
		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);
	
	if (bf == 0)
	{
		subR->_bf = 0;
		subRL->_bf = 0;
		parent->_bf = 0;
	}
	else if (bf == 1)
	{
		subRL->_bf = 0;
		subR->_bf = 0;
		parent->_bf = -1;
	}
	else if (bf == -1)
	{
		subRL->_bf = 0;
		subR->_bf = 1;
		parent->_bf = 0;
	}
	else
		assert(false);
}

我们可以看到,我们的旋转用的之前写好的左单旋和右单旋,然后我们只需要更新平衡因子就行了。其实双旋的平衡因子才是麻烦事我们再次用一般形式来进行计算。最后我们做出总结:只要看 第三个结点,也就是parent->left->right 或者事 parent->right->left 的平衡因子分为三种情况,大家可以多画画图就知道代码写的意思了。

两种种就是以上两张图片的情况,

第一种是为平衡因子为1的情况,

第二种是为-1的。都是分别进行左右双旋。

最后一种就是是插入之后,它的平衡因子为零,那么改动的三个结点:parent parent->left  parent->left->right(parent parent->right parnet->right->left) 的平衡因子都要成为0;

以上霓虹颜色的截图都来自于up主蓝不过海呀 的视频。

最后分享完整代码

		bool insert(const pair<K, V>& kv)
		{
			if (root == nullptr)
			{
				root = new Node(kv);
				return true;
			}
			Node* parent = nullptr;
			Node* cur = root;
			while (cur)
			{
				if (cur->_kv.first > kv.first)
				{
					parent = cur;
					cur = cur->left;
				}
				else if (cur->_kv.first < kv.first)
				{
					parent = cur;
					cur = cur->right;
				}
				else
				{
					cout << "已经重复了" << endl;
					return false;
				}
			}
			cur = new Node(kv);
			if (parent->_kv.first > cur->_kv.first)
			{
				parent->left = cur;
			}
			else
			{
				parent->right = cur;
			}
			cur->parent = parent;
			//更新平衡因子
			while (parent)
			{
				if (cur == parent->left)
				{
					parent->_bf--;
				}
				else
				{
					parent->_bf++;
				}
				if (parent->_bf == 0)
				{
					break;
				}
				else if (parent->_bf == -1 || parent->_bf == 1)
				{
					//祖先需要更新,所以父亲和cur网上挪,然后再次循环就会到2的条件
					cur = parent;
					parent = parent->parent;
					
				}
				else if(parent->_bf == 2|| parent->_bf == -2)
				{
					//翻转
                    //先是同号得				
					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
					{
						RotateRL(parent);
					}
					break;
				}
				else
				{
					assert(false);
				}
			}
			return true;
		}

		void RotateL(Node* parent)
		{
			Node* subR = parent->right;
			Node* subRL = subR->left;

			parent->right = subRL;
			if (subRL)
				subRL->parent = parent;

			Node* PParent = parent->parent;
			subR->left = parent;
			parent->parent = subR;

			if (PParent)
			{
				subR->parent = PParent;
				if (PParent->left == parent)
					PParent->left = subR;
				else
					PParent->right = subR;
			}
			else
			{
				subR->parent = nullptr;
				root = subR;
			}
			//平衡因子
			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* PParent = parent->parent;
			subL->right = parent;
			parent->parent = subL;
			if (PParent == nullptr)
			{
				root = subL;
				
			}
			else
			{
				if (PParent->right == parent)
					PParent->right = subL;
				else
					PParent->left = subL;
			}
			subL->parent = PParent;
			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);
			
			if (bf == 0)
			{
				subL->_bf = 0;
				subLR->_bf = 0;
				parent->_bf = 0;
			}
			else if (bf == 1)
			{
				subLR->_bf = 0;
				subL->_bf = -1;
				parent->_bf = 0;
			}
			else if (bf == -1)
			{
				subLR->_bf = 0;
				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);
			
			if (bf == 0)
			{
				subR->_bf = 0;
				subRL->_bf = 0;
				parent->_bf = 0;
			}
			else if (bf == 1)
			{
				subRL->_bf = 0;
				subR->_bf = 0;
				parent->_bf = -1;
			}
			else if (bf == -1)
			{
				subRL->_bf = 0;
				subR->_bf = 1;
				parent->_bf = 0;
			}
			else
				assert(false);
		}

  • 16
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值