Bootstrap

C++ 平衡二叉树旋转的实现


前言

随着C++学习了两个多月,进度也来到了map和set的模拟实现。
而其底层是用红黑树模拟实现的,但是红黑树的实现又借鉴了平衡二叉树。因此先来处理平衡二叉树的问题,最重要的也就是旋转问题。


一、旋转有几种情况?

1.左单旋

在这里插入图片描述
这两种情况都需要进行左单旋
不同的是第一种情况需要将根节点变化

2.右单旋

在这里插入图片描述
这两种情况都需要进行右单旋
不同的是第一种情况需要将跟根节点变换

3.左右双旋

在这里插入图片描述
在变换过程中只需注意新插入的结点是在b中插入还是c中插入
这个信息可以巧妙的通过记录60的平衡因子来获知

4.右左双旋

在这里插入图片描述
在变换过程中只需注意新插入的结点是在b中插入还是c中插入
这个信息可以巧妙的通过记录60的平衡因子来获知

二、具体实现

1.树节点的实现

template<class T>
struct TreeNode{
	TreeNode(const T& data)
	:_data(data),
	_left(nullptr),
	_right(nullptr),
	_parent(nullptr)
	{}

	T _data;
	TreeNode* _left;
	TreeNode* _right;
	TreeNode* _parent;
	int _bf;
};

这里的data表示树节点存的数据
然后分别有左 右 父亲 三个指针
最后 _bf表示当前结点的平衡因子是多少(以此来控制旋转)

2.旋转

注意我们的旋转只在平衡因子为2或者-2的结点旋转
并且每一次旋转完成之后 可以认为此时树中的所有结点已经平衡。

1.左单旋

void RotateL(Node* parent)
{
	Node* cur = parent->_right;
	Node* cur_left = cur->_left;
	Node* pparent = parent->_parent;

	parent->_right = cur_left;
	if (cur_left != nullptr)
		cur_left->_parent = parent;

	cur->_left = parent;
	parent->_parent = cur;

	if (pparent == nullptr)
	{
		_root = cur;
		cur->_parent = nullptr;
	}
	else
	{
		if (pparent->_left == parent)
		{
			pparent->_left = cur;
		}
		else
		{
			pparent->_right = cur;
		}
		cur->_parent = pparent;
	}
	cur->_bf = 0;
	parent->_bf = 0;
}

如果 pparent为空指针,即为第一种情况,需要将根节点=cur;
否则,则判断是左孩子还是有孩子,使其连接起来。
最后将这三个结点的平衡因子全赋为0。

2.右单旋

void RotateR(Node* parent)
{
	Node* cur = parent->_left;
	Node* cur_right = cur->_right;
	Node* pparent = parent->_parent;

	parent->_left = cur_right;
	if (cur_right != nullptr)
		cur_right->_parent = parent;

	cur->_right = parent;
	parent->_parent = cur;

	if (pparent == nullptr)
	{
		_root = cur;
		cur->_parent = nullptr;
	}
	else
	{
		if (pparent->_left ==parent)
		{
			pparent->_left = cur;
		}
		else
		{
			pparent->_right = cur;
		}
		cur->_parent = pparent;
	}
	cur->_bf = 0;
	parent->_bf = 0;
}

和 左单旋大差不差,在此不过多赘述。

3.左右双旋

void RotateLR(Node* parent)
{
	Node* cur = parent->_left;
	Node* cur_right = cur->_right;
	int bf = cur_right->_bf;

	RotateL(cur);
	RotateR(parent);

	if (bf == 0)
	{
		parent->_bf = 0;
		cur->_bf = 0;
		cur_right->_bf = 0;
	}
	else if (bf == 1)
	{
		parent->_bf = 0;
		cur->_bf = -1;
	}
	else if (bf == -1)
	{
		parent->_bf = 1;
		cur->_bf = 0;
	}
	else
	{
		assert(false);
	}
}

在此先直接调用左单旋和右单旋函数。
但是会出现一个问题,左单旋和右单旋函数会将平衡因子直接修改为0,这显然是错误的。
所以我们先记录下它的孩子结点,然后用bf记录cur_right结点的平衡因子,来判断是在60结点的左边还是右边插入的
若 bf=0,表示cur_right即为插入的结点,平衡因子全部修改为0;
若bf=1,表示插入到了右边;
若bf=-1,表示插入到了左边;
最后修改parent,cur对应的平衡因子即可。
至于为什么这么修改,读者可以去画图试一试。

4.右左双旋

void RotateRL(Node* parent)
{
	Node* cur = parent->_right;
	Node* cur_left = cur->_left;
	int bf = cur_left->_bf;

	RotateR(parent->_right);
	RotateL(parent);

	if (bf == 0)
	{
		parent->_bf = 0;
		cur->_bf = 0;
		cur_left->_bf = 0;
	}
	else if (bf == 1)
	{
		parent->_bf = -1;
		cur->_bf = 0;
	}
	else if (bf == -1)
	{
		parent->_bf = 0;
		cur->_bf = 1;
	}
	else
	{
		assert(false);
	}
}

和上述实现基本一样,在此不过多赘述。

三、总结

以上就是平衡二叉树旋转的实现。
完整代码存放在Git-ee上 平衡二叉树
学习任重而道远,接下来学习红黑树的插入!

;