平衡二叉树(AVLTree)的构造等——超详细

平衡二叉树概念

二叉搜索树虽可以缩短查找的效率,但如果数据有序或接近有序二叉搜索树将退化为单支树,查找元素相当 于在顺序表中搜索元素,效率低下。因此,两位俄罗斯的数学家G.M.Adelson-Velskii和E.M.Landis在1962年 发明了一种解决上述问题的方法:当向二叉搜索树中插入新结点后,如果能保证每个结点的左右子树高度之 差的绝对值不超过1(需要对树中的结点进行调整),即可降低树的高度,从而减少平均搜索长度。

一棵AVL树或者是空树,或者是具有以下性质的二叉搜索树:
1.它的左右子树都是AVL树
2.左右子树高度之差(简称平衡因子)的绝对值不超过1(-1/0/1)

平衡二叉树(AVLTree)的构造等——超详细_第1张图片
这里的使用右子树高度减去左子树高度。
如果一棵二叉搜索树是高度平衡的,它就是AVL树。如果它有n个结点,其高度可保持在O(logn) ,搜索时 间复杂度O(logn)。

AVLTree结点

每一个结点应当如下

template<class T)
struct AVLNode
{
	T _value;//结点的值
	int _bf;//平衡因子
	AVLNode<T>* _left;//指向左孩子
	AVLNode<T>* _right;//指向右孩子
	AVLNode<T>* _parent;//指向父结点
	AVLNode(const T& val = T())
		:_value(val)
		, _bf(0)
		, left((nullptr)
		, _right(nullptr)
		, _parent(nullptr)
	{}
}

AVLTree类

template <class T>
class AVLTree
{
public:
	typedef AVLNode<T> Node;
	AVLTree()
		:_root(nullptr)
	{}
private:
	Node* _root;
};

AVLTree旋转

插入的时候,极有可能会破坏平衡二叉树的平衡因子,为了使平衡二叉树这里将介绍左旋和右旋的概念。调整平衡二叉树一共有四种情况。
1. 新节点插入较高左子树的左侧—左左:右单旋
平衡二叉树(AVLTree)的构造等——超详细_第2张图片

步骤:
1.b结点的右子树连成a结点。
2.a结点的左子树连接成b结点的右子树
3.更新a,b结点的父结点(根节点需要跟新)。

void RotateR(Node* parent)//parent结点对应上图的a结点
{
		Node* subL = parent->_left;//记录parent的左孩子subL
		Node* subLR = subL->_right;//记录subL的右孩子subLR
		subL->_right = parent;//将subL的右指针指向父结点
		parent->_left = subLR;//跟新父结点的左子树
		//如果subLR不为空,则需要将其父指针指向parent
		if(subLR)
			sunLR->_parent = parent;
		//如果parent为根节点_root
		if (parent == _root)
		{
			_root = subL;
			subL->_parent = nullptr;//新的根结点父指针置空
		}
		//不是根结点
		else
		{
			Node* grand = parent->_parent;//记录父结点的父结点
			subL->_parent = grand;//更新subL的父结点
			//更新祖结点的左子树或右子树
			if(grand->_left == parent)
				grand->_left = subL;
			else
				grand->_right = subL;
		}
		parent->_parent = subL;//更新parent的父结点
		parent->_bf = subL->_bf = 0;//更新新的平衡因子
}

2. 新节点插入较高右子树的右侧—右右:左单旋
平衡二叉树(AVLTree)的构造等——超详细_第3张图片
左单旋与右单选较类似,这里就不再解释,附代码

void RotateL(Node* parent)//parent为上图的a结点
{
	Node* subR = parent->_right;//记录父结点的右子树
	Node* subRL = subR->_left;//记录subR的左子树
	subR->_left = parent;
	parent->_right = subRL;
	//subRL非空
	if(subRL)
		subRL->_parent = parent;
	//如果parent是根结点
	if(parent == _root)
	{
		subR = _root;
		subR->_parent = nullptr;
	}
	//如果不为空结点
	else
	{
		Node* grand = parent->_parent;//记录父结点的父亲
		subR->_parent = grand;
		//更新祖结点的左或右子树
		if(grand->_left == parent)
			grand->_left = subR;
		else
			grand->_right = subR;
	}
	//更新父结点的父亲
	parent->_parent = subR;
	subR->_bf = parent->_bf = 0;//更新平衡因子
}

3. 新节点插入较高左子树的右侧—左右:先左单旋再右单旋
平衡二叉树(AVLTree)的构造等——超详细_第4张图片
这里只需要判断插入时是哪种情况,复用上面的左旋和右旋代码即可
4.新节点插入较高右子树的左侧—右左:先右单旋再左单旋
平衡二叉树(AVLTree)的构造等——超详细_第5张图片
同上

AVLTree插入

bool insert(const T& val)
{
	//搜索树的插入
	if (_root == nullptr)
	{
		_root = new Node(val);
		return true;
	}
	Node* cur = _root;
	Node* parent = nullptr;
	while (cur)
	{
		parent = cur;
		if (cur->_value == val)
			return false;
		else if (cur->_value < val)
			cur = cur->_right;
		else
			cur = cur->_left;
	}
	cur = new Node(val);
	if (parent->_value < val)
		parent->_right = cur;
	else
		parent->_left = cur;
	cur->_parent = parent;
	//更新 + 调整
	while (parent)
	{
		// 1. 更新parent平衡因子
		if (parent->_left == cur)
			--parent->_bf;
		else
			++parent->_bf;
		//2. 判断是否需要继续更新
		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 && cur->_bf == -1)
			{
				//左边的左边高,右旋
				RotateR(parent);
			}
			else if (parent->_bf == 2 && cur->_bf == 1)
			{
				//右边的右边高, 左旋
				RotateL(parent);
			}
			else if (parent->_bf == 2 && cur->_bf == -1)
			{
				//右边的左边高: 右左双旋
				Node* subR = parent->_right;
				Node* subRL = subR->_left;
				int bf = subRL->_bf;

				RotateR(cur);
				RotateL(parent);

				//调整平衡因子
				if (bf == 1)
				{
					//subRL右子树高
					subR->_bf = 0;
					parent->_bf = -1;
				}
				else if (bf == -1)
				{
					//subRL左子树高
					parent->_bf = 0;
					subR->_bf = 1;
				}		
			}
			else if (parent->_bf == -2 && cur->_bf == 1)
			{
				//左边的右边高:左右双旋
				RotateL(cur);
				RotateR(parent);
			}
			break;
		}
	}
	return true;
}

AVLTree的遍历

void inoder()
{
	_inoder(_root);
}
void _inoder(Node* root)
{
	if(root)
	{
		_inoder(root->_left);
		cout<<root->_value<<" ";
		_inoder(root->_right);
	}
}

AVLTree平衡判断

bool isBalance()
{
	return _isBalance(_root);
}
bool _isBalance(Node* root)
{
	if (root == nullptr)
		return true;
	//左右子树高度差是否和平衡因子相等
	int subL = Height(root->_left);
	int subR = Height(root->_right);
	if (root->_bf != subR - subL)
	{
		cout << "节点:" << root->_value << "异常: bf: " << root->_bf << " 高度差:" << subR - subL << endl;
		return false;
	}
	//平衡因子的绝对值知否小于2
	return abs(root->_bf) < 2
		&& _isBalance(root->_left)
		&& _isBalance(root->_right);
}
//求高度
int Height(Node* root)
{
	if (root == nullptr)
		return 0;
	int left = Height(root->_left);
	int right = Height(root->_right);
	return left > right ? left + 1 : right + 1;
}

总结

总结: 假如以Parent为根的子树不平衡,即Parent的平衡因子为2或者-2,分以下情况考虑
1.Parent的平衡因子为2,说明Parent的右子树高,设Parent的右子树的根为SubR
a.当SubR的平衡因子为1时,执行左单旋
b.当SubR的平衡因子为-1时,执行右左双旋
2.Parent的平衡因子为-2,说明Parent的左子树高,设Parent的左子树的根为SubL
a.当SubL的平衡因子为-1是,执行右单旋
b.当SubL的平衡因子为1时,执行左右双旋

旋转完成后,原Parent为根的子树个高度降低,已经平衡,不需要再向上更新。

你可能感兴趣的:(C++,AVLTree平衡二叉树)