AVL树的旋转

AVL树节点中存有高度差作为平衡因子,高度差为右树高度-左数高度。

1、按照搜索树规则插入

2、插入节点后只会影响到该节点的祖先的平衡性,因此更新插入节点的祖先节点的平衡因子。

  1. 插入到父亲的左边,父亲的平衡因子- -。 插入到父亲的右边,父亲的平衡因子++。
  2. 如果父亲的平衡因子==0,父亲所在的子树高度不变,则不需要在更新祖先的平衡因子,插入结束。
  3. 如果插入以后,父亲的平衡因子是1或者-1,父亲所在的子树高度变了,继续往上更新。转到1处继续更新(就是说节点是1或者-1,就得继续向上找父亲,然后如果我是父亲的左树,父亲- -,我是父亲的右树,父亲++。)。
  4. 父亲的平衡因子为2或者-2,则父亲所在的子树已经不平衡了,需要旋转。

AVLTree的节点:需要有平衡因子,左节点,右节点,父节点的信息,AVL中存的是pair类型的kv键值对。

AVLTree的插入:插入就按照二叉搜索树插入节点,但是插入完成后需要把当前节点的父节点给指明,avl树的节点是三个链,左右父。

节点先插入后:

  1. 需要判断插入节点的方向,并对父节点及其祖先的平衡因子进行计算。
  2. 更新双亲的平衡因子,
  3. 判断平衡因子的值,如果插入前父亲的平衡因子是0,插入后父亲的平衡因子为1 或者 -1,说明以父亲为根的二叉树的高度增加了一层,因此需要继续向上调整。如果平衡因子的值为2或者-2,说明需要旋转调整。如果插入后父亲的平衡因子为0就终止。

AVL树的旋转:

1:新插入的子树在左子树的左侧,左左 - - 右单旋

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

  1. 30节点的右孩子可能存在,也可能不存在
  2. 60可能是根节点,也可能是子树如果是根节点,旋转完成后,要更新根节点如果是子树,可能是某个节点的左子树,也可能是右子树

当插入的节点是在左边,父节点也是左子树,影响了平衡,就是 左左- - - 右单旋的思路:

传入bf=-2的节点为parent,让subl=parent->_left,sublr=subl->_right。

1.如果sublr不等于Nullptr,则让sublr->_parent=parent。

2.subl->_right=parent,parent->_parent=subl。

完成1,2旋转就结束了,但是需要注意:

parent有可能是root也可能不是root,如果是root,直接让root = subl。

如果不是root,让 ppnode = parent->_parent:

需要先判断 parent 是它父亲的左子树还是右子树,

如果是左子树:让ppnode->_left = subl,如果是右子树:ppnode->_right = subl。

最后让subl->_parent = ppnode。

最后让parent->_bf=subl->_bf=0。

具体分析如下图:

如果是插入的节点在右边,父节点也是右子树,影响了平衡,就是右右 - - - 左单旋。

思路和右单旋相同,如图:

如果插入的节点在比较深的子树的右侧,构成一个折线型的结构,60=-1,30=1,90=-2,就是一个右左右的不平衡,需要先左在右旋转

其中,新插入的节点如果在b的下边,刚开始60->bf = = -1,则旋转完成后 需要设置parent->bf = = 1。

但也有可能插入在c的下边,如果在c的下边,则刚开始 60->bf == 1,旋转完成后需要设置 subl->bf = = -1。

对30左单旋后,subl、sublr的 bf 设置为0,对90右单旋后,会把sublr、parent 的 bf 设置为0。因此,在旋转之前需要先拿到 sublr 对应的 bf ,旋转完成后才可以正确更新parent 或者 subl 的bf。

下图所示的为新增节点在 b 的下边,但实际代码需要考虑在c下边的情况。

如果新插入的节点在较深右子树的左侧:**是一个左右左结构,先右单旋再左单旋。**理解上和先左再右一样,分析如下图:

验证是否为平衡树,需要计算当前节点的左子树高度和右子树高度,然后右高度-左高度得到的值判断和bf是否相等,不相等就不平衡。

下图为求树节点个数和树最大深度的普遍写法:

完整的AVL旋转代码如下:

cpp 复制代码
template<class K, class V>
struct AVLTreeNode
{
	AVLTreeNode<K, V>* _left;
	AVLTreeNode<K, V>* _right;
	AVLTreeNode<K, V>* _parent;
	pair<K, V> _kv;
	int _bf;
	AVLTreeNode(const pair<K,V>& kv)
		:_left(nullptr)
		, _right(nullptr)
		, _parent(nullptr)
		, _kv(kv)
		, _bf(0)
	{}
};
template<class K, class V>
class AVLTree
{
	typedef AVLTreeNode<K, V> Node;
public:
	AVLTree()
		:_root(nullptr)
	{}

	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->_right;
			}
			else if (cur->_kv.first > kv.first)
			{
				parent = cur;
				cur = cur->_left;
			}
			else
			{
				return false; // 默认情况下不允许有相同的值在二叉搜索树中
			}
		}
		cur = new Node(kv);
		if (parent->_kv.first < kv.first)
		{
			parent->_right = cur;
		}
		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)
			{
				// 插入前双亲的平衡因子是0,插入后双亲的平衡因为为1 或者 -1 ,
				//说明以双亲为根的二叉树 的高度增加了一层,因此需要继续向上调整
				cur = parent;
				parent = parent->_parent;
			}
			else if (parent->_bf == 2 || parent->_bf == -2)
			{
				//当前子树不平衡了,因此需要旋转。

				//如果是当前节点为-1,parent=-2 相当于连续在左 左 的下面插入了一个,则需要右单旋
				if (cur->_bf == -1 && parent->_bf == -2)
				{
					RotateR(parent);
				}
				//如果是当前节点为1,parent=2 相当于连续在右 右 的下面插入了一个,则需要左单旋
				else if (cur->_bf == 1 && parent->_bf == 2)
				{
					RotateL(parent);
				}
				else if (parent->_bf == -2 && cur->_bf == 1)
				{
					RotateLR(parent);
				}
				else if (parent->_bf == 2 && cur->_bf == -1)
				{
					RotateRL(parent);
				}

				break;
			}
			else //理论上不可能走到这一度,但是得写防止自己写的代码出问题
			{
				assert(false);
			}
		}


		return true;
	}


	void RotateR(Node* parent)
	{
		Node* subL = parent->_left;
		Node* subLR = subL->_right;

		parent->_left = subLR;

		if (subLR)
			subLR->_parent = parent;

		subL->_right = parent;

		Node* ppnode = parent->_parent; //得先把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 = subL->_bf = 0;
	}

	void RotateL(Node* parent)
	{
		Node* subR = parent->_right;
		Node* subRL = subR->_left;

		parent->_right = subRL;
		if (subRL)
			subRL->_parent = subR;

		subR->_left = parent;
		Node* ppnode = parent->_parent;
		parent->_parent = subRL;

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

	void RotateLR(Node* parent)
	{
		Node* subL = parent->_left;
		Node* subLR = subL->_right;

		int bf = subLR->_bf;
		RotateL(subL);
		RotateR(parent);

		if (bf == 1)
			subL->_bf = -1;
		else
			parent->_bf = 1;
	}

	void RotateRL(Node* parent)
	{
		Node* subR = parent->_right;
		Node* subRL = subR->_left;

		int bf = subRL->_bf;

		RotateR(subR);
		RotateL(parent);

		if (bf == -1)
			subR->_bf = 1;
		if (bf == 1)
			parent->_bf = -1;
	}

	void InOrder()
	{
		_InOrder(_root);
		cout << endl;
	}
	Node* Find(const K& key) // k v 类型,通过k找到v并可以得到key的节点
	{
		Node* cur = _root;
		while (cur)
		{
			if (cur->_kv.first < key)
			{
				cur = cur->_right;
			}
			else if (cur->_kv.first > key)
			{
				cur = cur->_left;
			}
			else
			{
				return cur;
			}
		}
		return nullptr;
	}

	bool IsBalance() //判断是否平衡
	{
		return _IsBalance(_root);
	}
	int Height() //判断当前节点的深度
	{
		return _Height(_root);
	}
	int Size()
	{
		return _size(_root);
	}

private:

	int _Size(Node* root)
	{
		return root == nullptr ? 0 : _Size(root->_left) + _Size(root->_right) + 1;
	}
	int _Height(Node* root)
	{
		if (root == nullptr)
			return 0;
		return max(_Height(root->_left), _Height(root->_right)) + 1;
	}

	bool _IsBalance(Node* root)
	{
		if (root == nullptr)
			return true;
		int leftHeight = _Height(root->_left);
		int rightHeight = _Height(root->_right);

		//平衡因子是否合理,不合理打印出不合理的那个点:
		if (abs(leftHeight - rightHeight) >= 2)
		{
			cout << "平衡因子不合理: " << root->_kv.first << " bf = " << root->_bf << endl;
			return false;
		}
		//检查平衡因子是否正确,不正确打印出不正确的那个点
		if (rightHeight - leftHeight != root->_bf)
		{
			cout << "平衡因子不正确: " << root->_kv.first << " bf = " << root->_bf << endl;
			return false;
		}
		return _IsBalance(root->_left) 
			&& _IsBalance(root->_right);
	}

	void _InOrder(Node* root)
	{
		if (root == nullptr)
		{
			return;
		}
		_InOrder(root->_left);
		cout << root->_kv.first << ":" << root->_kv.second << endl;
		_InOrder(root->_right);
	}

private:
	Node* _root;
};
相关推荐
‘’林花谢了春红‘’3 小时前
C++ list (链表)容器
c++·链表·list
机器视觉知识推荐、就业指导4 小时前
C++设计模式:建造者模式(Builder) 房屋建造案例
c++
Yang.996 小时前
基于Windows系统用C++做一个点名工具
c++·windows·sql·visual studio code·sqlite3
熬夜学编程的小王6 小时前
【初阶数据结构篇】双向链表的实现(赋源码)
数据结构·c++·链表·双向链表
zz40_6 小时前
C++自己写类 和 运算符重载函数
c++
六月的翅膀7 小时前
C++:实例访问静态成员函数和类访问静态成员函数有什么区别
开发语言·c++
liujjjiyun7 小时前
小R的随机播放顺序
数据结构·c++·算法
¥ 多多¥7 小时前
c++中mystring运算符重载
开发语言·c++·算法
天若有情6738 小时前
c++框架设计展示---提高开发效率!
java·c++·算法