c  avl树的两单旋和两双旋的项目实践-kb88凯时官网登录

来自:网络
时间:2024-06-09
阅读:

如果在一棵原本是平衡的avl树中插入一个新节点,可能造成不平衡,此时必须调整树的结构,使之平衡化。根据节点插入位置的不同,avl树的旋转分为四种。

1. 新节点插入较高左子树的左侧---左左:右单旋

c   avl树的两单旋和两双旋的项目实践

a/b/c分别是高度为h的avl子树

上图在插入前,avl树是平衡的,新节点插入到30的左子树(注意:此处不是左孩子)中,30左子树增加了一层,导致以60为根的二叉树不平衡,要让60平衡,只能将60左子树的高度减少一层,右子树增加一层,即将左子树往上提,这样60转下来,因为60比30大,只能将其放在30的右子树,而如果30有右子树,右子树根的值一定大于30,小于60,只能将其放在60的左子树,旋转完成后,更新节点的平衡因子即可。在旋转过程中,有以下几种情况需要考虑:
1. 30节点的右孩子可能存在,也可能不存在
2. 60可能是根节点,也可能是子树
如果是根节点,旋转完成后,要更新根节点
如果是子树,可能是某个节点的左子树,也可能是右子树

代码

//右单旋
void rotater(node* parent)
{
	node* subl = parent->_left;
	node* sublr = subl->_right;
	parent->_left = sublr;
	if (sublr)
	{
		subl->_right = parent;
	}
	subl->_right = parent;
	node* ppnode = parent->_parent;
	parent->_parent = subl;
	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 = 0;
	subl->_bf = 0;
}

2. 新节点插入较高右子树的右侧---右右:左单旋

c   avl树的两单旋和两双旋的项目实践

左单旋与右单旋的操作类似,只有左右节点的区别

 代码

//左单旋
void rotatel(node* parent)
{
	node* subr = parent->_right;
	node* subrl = subr->_left;
	parent->_right = subrl;
	if (subrl)
	{
		subr->_left = 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;
	
}

3. 新节点插入较高左子树的右侧---左右:先左单旋再右单旋

c   avl树的两单旋和两双旋的项目实践

参考30和60的相对位置,将双旋变成单旋后再旋转,即:先对30进行左单旋,然后再对90进行右单旋,旋转完成后再考虑平衡因子的更新。

代码 

//左右单旋
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 = 0;
		sublr->_bf = 1;
	}
	else if (bf == -1)
	{
		parent->_bf = 0;
		subl->_bf = -1;
		sublr->_bf = 0;
	}
	else if(bf==0)
	{
		parent->_bf = 0;
		subl->_bf = 0;
		sublr->_bf = 0;
	}
	else
	{
		assert(false);
	}
}

4. 新节点插入较高右子树的左侧---右左:先右单旋再左单旋

c   avl树的两单旋和两双旋的项目实践

代码

//右左单旋
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 = 0;
		subrl->_bf = 1;
	}
	else if (bf == -1)
	{
		parent->_bf = 0;
		subr->_bf = -1;
		subrl->_bf = 0;
	}
	else if (bf == 0)
	{
		parent->_bf = 0;
		subr->_bf = 0;
		subrl->_bf = 0;
	}
	else
	{
		assert(false);
	}
}

总结:
假如以pparent为根的子树不平衡,即pparent的平衡因子为2或者-2,分以下情况考虑:
1. pparent的平衡因子为2,说明pparent的右子树高,设pparent的右子树的根为psubr。
当psubr的平衡因子为1时,执行左单旋。
当psubr的平衡因子为-1时,执行右左双旋。
2. pparent的平衡因子为-2,说明pparent的左子树高,设pparent的左子树的根为psubl。
当psubl的平衡因子为-1是,执行右单旋。
当psubl的平衡因子为1时,执行左右双旋。
旋转完成后,原pparent为根的子树个高度降低,已经平衡,不需要再向上更新。

返回顶部
顶部
网站地图