AVLTree——(高度)平衡二叉搜索树

AVL树

1.首先AVL树是一个二叉搜索树

2.其次AVL树在高度上平衡,也就是说:树的左右子树的高度差不超过1,树的左子树和右子树的高度也不超过1。


AVL树的原理:

在二叉搜索树的基础上,每当插入一个节点,更新他的平衡因子,如果平衡因子没有违反规则,则插入结束,否则通过"旋转"操作,实现平衡同时不破坏搜索树的条件。

如果在某个位置插入节点,只会影响他的祖先的平衡因子(父亲,父亲的父亲,....)


AVL树的实现

AVL树我们重点实现他的插入,因为删除的实现与插入的实现类似,而查找的实现与二叉搜索树中的查找类似。

由于AVL树本身是一种二叉搜索树,因此其在实现上与二叉搜索树有很多相近之处。

由于需要对更新平衡因子,对不平衡节点旋转操作,所以我们维护一个parent指针指向当前节点的父亲节点,我们还要给每个节点新增一个变量存储平衡因子的值。


插入一个节点时,我们首先不考虑AVL树的平衡问题,像二叉搜索树一样,通过大小的比较,找到合适插入的空位置,然后插入该节点。这时,由于新结点的插入,该节点的祖先节点的平衡因子可能会改变,二叉树的平衡结构可能被破坏,因此我们首先更新其祖先节点的平衡因子。

平衡因子的更新

在父亲节点处插入节点,插入的是左节点平衡因子就-1,右节点平衡因子就+1

如果插入后父亲节点的平衡因子为0,证明原来其只有一个左节点或右节点,插入后弥补了空缺,不影响平衡。

如果插入后父亲的平衡因子为1/-1,证明原来其没有子节点,插入了一个子节点导致平衡因子加或者减1,这时导致了其高度的改变,并且可能影响了父亲节点的祖先节点,需要继续向上更新平衡因子:如果父亲节点是父亲节点的父亲节点的右/左,就把父亲节点的父亲节点的平衡因子加/减1,如果运算后其平衡因子为0,则判断结束,因为不会继续向上影响,如果为1/-1,则继续向上更新平衡因子,如果为2/-2,则说明在此节点的位置开始不平衡,则对此节点进行旋转操作,操作后二叉树平衡。循环往复。

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
		{
			return false;
		}
	}
	cur = new Node(kv);
	if (parent->_kv.first < kv.first)
	{
		parent->_right = cur;
		cur->_parent = parent;
	}
	else
	{
		parent->_left = 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 = parent;
			parent = parent->_parent;
		}
		else if (parent->_bf == 2 || parent->_bf == -2)//找到不平衡节点
		{
			//旋转处理
			if (parent->_bf == 2)
			{
				if (cur->_bf == 1)
				{
					RotateL(parent);//左单旋
				}

				else if (cur->_bf == -1)
				{
					RotateRL(parent);//右左双旋
				}
			}
			else if (parent->_bf == -2)
			{
				if (cur->_bf == -1)
				{
					RotateR(parent);//右单旋
				}

				else if (cur->_bf == 1)
				{
					RotateLR(parent);//左右双旋
				}
			}
			break;
		}
		
	}

	return true;
}

AVL树的重点和难点是在不平衡节点处通过旋转操作,使整个二叉树变平衡

旋转操作可以根据情况不同细分为:左单旋,右单旋,左右双旋,右左双旋

单旋的路径为直线,双旋的路径为折线


当插入节点在较高右子树的右侧时,进行左单旋

subR的左给到parent的右,parent变成subR的左

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 = parent->_parent;
	}
	
	parent->_bf = subR->_bf = 0;
}

当新插入节点在较高左子树的左侧时,进行右单旋

subL的右给到parent的左,parent变成subL的右

void RotateR(Node* parent)
{
	Node* subL = parent->_left;
	Node* ppNode = parent->_parent;
	Node* subLR = subL->_right;
	parent->_left = subLR;
	if (subLR)
	{
		subLR->_parent = parent;
	}
	parent->_parent = subL;
	subL->_right = parent;
	if (parent == _root)
	{
		_root == subL;
		subL->_parent == nullptr;
	}
	else
	{
		if (ppNode->_left == parent)
			ppNode->_left == subL;
		else
			ppNode->_right == subL;
		subL->_parent = ppNode;
	} 

	parent->_bf = subL->_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 == 1)
	{
		parent->_bf = 0;
		subL->_bf = -1;
		subLR->_bf = 0;
	}
	else if (bf == -1)
	{
		parent->_bf = 1;
		subL->_bf = 0;
		subLR->_bf = 0; 
	}
	else if (bf == 0)
	{
		parent->_bf = 0;
		subL->_bf = 0;
		subLR->_bf = 0;
	}
}

当新插入节点在较高右子树的左侧时,进行右左单旋(先右单旋,再左单旋)

void RotateRL(Node* parent)
{
	Node* subR = parent->_right;
	Node* subRL = subR->_left;
	int bf = subRL->_bf;
	RotateR(parent->_right);
	RotateL(parent);
	if (bf == -1)
	{
		parent->_bf = 0;
		subR->_bf = 1;
		subRL->_bf = 0;
	}
	else if (bf == 1)
	{
		subR->_bf = 0;
		parent->_bf = -1;
		subRL->_bf = 0;
	}
	else if (bf == 0)
	{
		subR->_bf = 0;
		parent->_bf = 0;
		subRL->_bf = 0;
	}
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值