今天你学C++了吗?——AVL树

06-01 871阅读

今天你学C++了吗?——AVL树


♥♥♥~~~~~~欢迎光临知星小度博客空间~~~~~~♥♥♥

♥♥♥零星地变得优秀~也能拼凑出星河~♥♥♥

♥♥♥我们一起努力成为更好的自己~♥♥♥

♥♥♥如果这一篇博客对你有帮助~别忘了点赞分享哦~♥♥♥

♥♥♥如果有什么问题可以评论区留言或者私信我哦~♥♥♥

✨✨✨✨✨✨ 个人主页✨✨✨✨✨✨

        在前面的博客中,我们已经学习了很多C++里面的容器,这一篇博客我们开始学习AVL树,准备好了吗~我们发车去探索C++的奥秘啦~🚗🚗🚗🚗🚗🚗

目录

AVL的概念

        AVL树的结构

AVL树的插入

 AVL树插入值流程

平衡因子更新

更新原则

更新停止条件

插入结点和平衡因子更新代码实现

旋转处理

旋转的原则

左单旋(右边高往左旋)

右单旋(左边高往右旋)

左右双旋

右左双旋

AVL树的查找

总代码


AVL的概念

        什么是AVL呢?

        AVL树是一种自平衡的二叉搜索查找树,在C++中常用于实现高效的动态集合操作,例如插入、删除和查找。AVL树得名于它的发明者G. M. Adelson-Velsky和E. M. Landis,他们于1962年首次提出这一数据结构。AVL树的出现是为了控制搜索二叉树的平衡,所以AVL树引入了一个平衡因子的概念~

  • AVL树要求任何节点的两个子树的高度差(称为平衡因子)的绝对值不超过1。
  • 平衡因子定义为:平衡因子 = 左子树高度 - 右子树高度。(当然也可以是【右子树高度 - 左子树高度】)
  • 如果插入或删除节点导致树的平衡性被破坏,AVL树会通过旋转操作(单旋转或双旋转)重新恢复平衡,这个是我们后面的重点讲解部分~

            有的人可能会说,既然是平衡的,那么为什么不是高度差为0呢?这一点事实上是很难做到的,因为要保证每一个节点高度差为0,那么只有满二叉树才可以满足这个条件了~这样不就只可以表示满二叉树了吗!高度差为0看似完美,但在实际节点数分布下(如2个节点、4个节点等)往往无法实现。所以高度差不超过1的规则,既保证了树的平衡性,又允许树根据节点数灵活调整结构,避免因过度限制导致构建失败。这种设计在保持高效操作的同时,实现了平衡性与灵活性的统一!

            我们可以来看看例子:(以【右子树高度 - 左子树高度】为标准)

    不难看出下面的这样一颗二叉树就是AVL树,每一个结点的左右子树高度差都不大于1~ 

    今天你学C++了吗?——AVL树

            

           但是下面的这一棵二叉树就不是AVL树,结点10的左右子树高度差为2,不满足条件~

    今天你学C++了吗?——AVL树

            AVL树的结构

            结合前面博客中二叉搜索树的结构二叉搜索树,那么AVL树是平衡的二叉搜索树,那么肯定有二叉搜索树的大体结构~

    二叉搜索树的结点应该保存三个数据:

    1、当前结点保存的数据

    2、当前结点的左子树地址

    3、当前结点的右子树地址

    AVL树结点还需要添加部分内容,一个是平衡因子,一个是双亲指针~

    平衡因子好理解,双亲指针有什么作用呢?在后面我们会进行解释~

    知道了结点的定义,我们就可以这样定义AVL树的结构:

    //AVL树结点结构
    template
    struct AVLTreeNode
    {
    	pair _kv;//保存的数据类型
    	AVLTreeNode* _left;//左孩子结点指针
    	AVLTreeNode* _right;//右孩子结点指针
    	AVLTreeNode* _parent;//双亲结点指针
    	int _bf;//平衡因子
    	//初始化
    	AVLTreeNode(const pair& kv)
    		:_kv(kv),
    		_left(nullptr),
    		_right(nullptr),
    		_parent(nullptr),
    		_bf(0) 
    	{
    	}
    };
    //AVL树结构
    template
    class AVLTree
    {
    	typedef AVLTreeNode Node;
    private:
    	Node* _root = nullptr;//根节点
    public:
    	//功能实现
    };

            知道了AVL树的结构,接下来我们就可以对AVL树进行插入,删除等操作了

    AVL树的插入

           我们首先来看看对AVL树的插入数据操作~

     AVL树插入值流程

    1. 插入操作:依据二叉搜索树的规则来插入一个值。
    2. 平衡因子更新:新结点插入后,仅会对祖先结点的高度产生影响,进而可能改变部分祖先结点的平衡因子。因此,需要沿着从新增结点到根结点的路径更新平衡因子。在最坏的情况下,可能需要一直更新到根结点;而在某些情况下,更新到中间某个结点就可以停止了,在后续会详细分析具体情况。
    3. 插入结束条件:
      • 若在更新平衡因子的过程中没有出现不平衡的情况,那么插入操作结束。
      • 若在更新平衡因子的过程中出现了不平衡,对不平衡的子树进行旋转操作。旋转操作在调整平衡的同时,会降低子树的高度,使得不再影响上一层结点,此时插入操作结束。

    平衡因子更新

    更新原则

    1. 我们实现的AVL树平衡因子的计算公式为:平衡因子 = 右子树高度 - 左子树高度。
    2. 只有当子树的高度发生变化时,才会影响当前结点的平衡因子。
    3. 插入结点会导致高度增加,所以:
      • 若新增结点位于父结点(parent)的右子树,那么父结点的平衡因子加1(parent的平衡因子++)。
      • 若新增结点位于父结点(parent)的左子树,那么父结点的平衡因子减1(parent平衡因子--)。
      • 父结点所在子树的高度是否发生变化,决定了是否需要继续向上更新平衡因子。

    更新停止条件

    1. 平衡因子变为0的情况:当更新操作完成后,若父结点(parent)的平衡因子等于0,且在更新过程中其平衡因子变化为 -1→0 或者 1→0,这表明在更新之前,父结点的子树呈现一边高一边低的状态,而新增结点被插入到了较低的那一边。如此一来,插入操作后父结点所在的子树高度并未发生改变,也就不会对父结点的父结点的平衡因子产生影响,此时更新操作可以结束。今天你学C++了吗?——AVL树
    2. 平衡因子变为1或 -1的情况:更新结束后,若父结点的平衡因子为1或 -1,且在更新前和更新过程中其平衡因子变化为 0→1 或者 0→ -1,这意味着在更新之前,父结点的子树两边高度是相等的。然而,新增结点插入之后,父结点所在的子树出现了一边高一边低的情况。虽然此时父结点所在的子树仍然符合平衡要求,但是子树的高度增加了1,这会对父结点的父结点的平衡因子产生影响,所以需要继续向上进行更新操作。今天你学C++了吗?——AVL树
    3. 平衡因子变为2或 -2的情况:更新完成后,若父结点的平衡因子为2或 -2,且在更新前和更新过程中其平衡因子变化为 1→2 或者 -1→ -2,这说明在更新之前,父结点的子树就是一边高一边低的状态,而新增结点插入到了较高的那一边,导致父结点所在的子树较高的那一边变得更高,从而破坏了子树的平衡状态,父结点所在的子树不再符合平衡要求,需要进行旋转处理。旋转操作有两个目标:一是将父结点的子树调整至平衡状态;二是降低父结点子树的高度,使其恢复到插入结点之前的高度。因此,旋转操作完成后,无需再继续向上进行更新,插入操作结束。(旋转策略我们后面会重点讲解)今天你学C++了吗?——AVL树
    4. 更新到根结点的情况:在不断向上更新的过程中,如果更新操作一直进行到根结点,即便根结点的平衡因子为1或 -1,更新操作也会停止。

    插入结点和平衡因子更新代码实现

            有了上面的这些画图理解和理论基础,接下来我们来进行代码实现:

    //插入结点
    bool insert(const pair& kv)
    {
    	if (_root == nullptr)//不存在根结点
    	{
    		//插入结点就是根结点
    		_root = new Node(kv);
    		return true;//插入成功
    	}
    	//存在根结点——找到应该插入的位置
    	Node* parent = nullptr;
    	Node* cur = _root;
    	while (cur)
    	{
    		if (kv.first _kv.first)//比当前结点小,插入当前结点左子树
    		{
    			parent = cur;
    			cur = cur->_left;
    		}
    		else if (kv.first > cur->_kv.first)//比当前结点大,插入当前结点右子树
    		{
    			parent = cur;
    			cur = cur->_right;
    		}
    		else//相等就不进行插入,插入失败
    		{
    			return false;
    		}
    	}
    	//parent即为插入结点父结点
    	cur = new Node(kv);
    	//判断是插入父结点左边还是右边
    	if (kv.first _kv.first)
    	{
    		parent->_left = cur;
    	}
    	else if (kv.first > parent->_kv.first)
    	{
    		parent->_right = cur;
    	}
    	cur->_parent = parent;
    	//平衡因子更新
    	while (parent)
    	{
    		//我们实现的AVL树平衡因子的计算公式为:平衡因子 = 右子树高度 - 左子树高度
    		if (parent->_left == cur)
    			//插入结点在左子树,父结点平衡因子--
    		{
    			parent->_bf--;
    		}
    		else if (parent->_right == cur)
    			//插入结点在右子树,父结点平衡因子++
    		{
    			parent->_bf++;
    		}
    		//分情况讨论
    		//1、父结点平衡因子更新为0,不需要继续向上更新
    		if (parent->_bf == 0)
    			break;
    		//2、父结点平衡因子更新为-1或者1,需要继续向上更新
    		//如果当前父结点为根结点,parent会到空,跳出循环
    		else if (parent->_bf == -1 || parent->_bf == 1)
    		{
    			cur = parent;
    			parent = parent->_parent;
    		}
    		//3、父结点平衡因子更新为-2或者2,已经不平衡需要旋转处理
    		else if (parent->_bf == -2 || parent->_bf == 2)
    		{
    			//旋转处理
    			break;
    		}
    		//其他情况,说明更新有问题
    		else
    		{
    			assert(false);
    		}
    	}
    	//更新结束
    	return true;
    }

            总的逻辑写完了,接下来就是我们的旋转处理了,我们继续来看看~

    旋转处理

            在前面我们已经进行了平衡因子的更新,我们可以发现当有结点的平衡因子达到2或者-2的时候,这个时候就不是平衡二叉树了,那么我们就需要进行旋转处理~

    旋转的原则

    1. 保持搜索树的规则(高度差_right;//cur Node* subRL = subR->_left;//B //更改指向 subR->_left = parent; parent->_right = subRL; //修改变动结点(subR,subRL,parent)的parent if(subRL)//subRL可能为空 subRL->_parent = parent; parent->_parent = subR; //subR可能成为整棵树的根结点,需要进行判断更新subR的parent Node* parentParent = parent->_parent; if (parentParent == nullptr) { //subR成为整棵树的根结点 _root = subR; subR->_parent = nullptr; } else { if (parentParent->_left == parent)//判断原节点是父亲的左/右结点 { parentParent->_left == subR; } else { parentParent->_right = subR; } //更新subR父结点 subR->_parent = parentParent; } //更新平衡因子 parent->_bf = subR->_bf = 0; }

      右单旋(左边高往右旋)

              相信有了左单旋的基础,右单旋就更加简单了~

      我们依然进行画图理解:

      今天你学C++了吗?——AVL树

      前面我们提到过h是可以代表任意高度的,这里我们讨论几种情况:

      情况一:h==0

      今天你学C++了吗?——AVL树

      情况二:h==1

      今天你学C++了吗?——AVL树

      情况三:h==2

      今天你学C++了吗?——AVL树

      情况四:h==3

      今天你学C++了吗?——AVL树

              我们可以发现不论是哪一种情况,都是可以进行相同的右单旋操作,也就有了我们的通用方案~

      今天你学C++了吗?——AVL树

      接下来,我们就进行代码实现:

      void RotateR(Node* parent)
      {
      	Node* subL = parent->_left;
      	Node* subLR = subL->_right;//b
      	//旋转操作
      	parent->_left = subLR;
      	subL->_right = parent;
      	//更新相应结点parent
      	if (subLR)//h可能等于0,那么subLR可能为空
      		subLR->_parent = parent;
      	
      	Node* parentParent = parent->_parent;//保存父结点的父亲
      	if (parentParent == nullptr)//说明原来的父结点是根结点
      	{
      		_root = subL;
      		subL->_parent = nullptr;
      	}
      	else
      	{
      		if (parentParent->_left == parent)//父结点是左孩子
      		{
      			parentParent->_left = subL;
      		}
      		else
      		{
      			//父结点是右孩子
      			parentParent->_right = subL;
      		}
      		subL->_parent = parentParent;
      	}
      	parent->_parent = subL;
      	//更新平衡因子
      	subL->_bf = parent->_bf = 0;
      }

      代码大致上是差不多的,只是逻辑上有一些区别~

      有了左单旋和右单旋,接下来,我们来看看更加复杂的情况~

      左右双旋

              在有些情况下,我们仅仅进行左单旋或者右单旋是无法解决问题的,比如说下面的例子~

      示例:不再是单纯的左边高,结点10是左边高,而结点5是右边高~也就可以发现插入节点之后parent和cur结点平衡因子异号~

      今天你学C++了吗?——AVL树

      今天你学C++了吗?——AVL树

      显然进行简单的单旋操作就不能解决问题了~单旋之后依然是不平衡的~

      我们继续以一个通用的例子进行分析:

      今天你学C++了吗?——AVL树

              我们可以看到在b子树新增加结点导致5结点右边高,10结点左边高,简单的单旋无法重新设置平衡,我们需要进行双旋,双旋就是利用两次单旋,经过第一次单旋让10结点变成单纯的左边高,再一次单旋就可以达到平衡~

      我们对b子树进行进一步的拆分

      今天你学C++了吗?——AVL树

      b子树新增加的结点点有下面的三种情况,我们来进行逐一分析:

      情况一:新增加结点在e子树

      今天你学C++了吗?——AVL树

      第一步:以5(subL)为旋转点进行左单旋

      今天你学C++了吗?——AVL树

      今天你学C++了吗?——AVL树

      第二步;以10结点(parent)为旋转点进行右单旋

      今天你学C++了吗?——AVL树

      经过两步单旋操作之后,这就平衡了~

      情况二:新增加结点在f子树

      今天你学C++了吗?——AVL树

      情况三:新增结点就是b子树(也就是h==0)

      今天你学C++了吗?——AVL树

              这三种情况都是先进行左单旋再进行右单旋,操作是一样的,那么我们就可以进行代码复用,使用前面的左右单旋代码,这里比较麻烦的是平衡因子的更新~

              我们可以看到三种情况的平衡因子是不一样的,我们可以根据结点插入后subLR的平衡因子来进行分情况讨论更新平衡因子~

      代码实现:

      void RotateLR(Node* parent)
      {
      	Node* subL = parent->_left;
      	Node* subLR = subL->_right;
      	//根据没有旋转前subLR的平衡因子分情况讨论
      	int bf = subLR->_bf;
      	
      	//先以subL为旋转点左旋
      	RotateL(subL);
      	//再以parent为旋转点右旋
      	RotateR(parent);
      	//每一个结点父结点已经更新,只需要修改平衡因子
      	if (bf == -1)//插入在e子树
      	{
      		subLR->_bf = 0;
      		subL->_bf = 0;
      		parent->_bf = 1;
      	}
      	else if (bf == 1)//插入在f子树
      	{
      		subLR->_bf = 0;
      		subL->_bf = -1;
      		parent->_bf = 0;
      	}
      	else if (bf == 0)//插入在b子树自己
      	{
      		subLR->_bf = 0;
      		subL->_bf = 0;
      		parent->_bf = 0;
      	}
      	else//其他情况,说明有问题
      	{
      		assert(false);
      	}
      }

      右左双旋

              知道了左右双旋,那么右左双旋就好分析了~

      我们同样用一个通用的例子来进行分析:

      今天你学C++了吗?——AVL树

      我们直接画图理解

      对b子树进行进一步拆分:

      今天你学C++了吗?——AVL树

      b子树新增加的结点点有下面的三种情况,我们来进行逐一分析:

      情况一:新增加结点在e子树

      今天你学C++了吗?——AVL树

      情况二:新增加结点在f子树

      今天你学C++了吗?——AVL树

      第一步:以15(subR)为旋转点进行右单旋

      今天你学C++了吗?——AVL树

      今天你学C++了吗?——AVL树

      第二步;以10结点(parent)为旋转点进行左单旋

      今天你学C++了吗?——AVL树

      完整图

      今天你学C++了吗?——AVL树

      情况三:新增结点就是b子树(也就是h==0)

      今天你学C++了吗?——AVL树

              这三种情况都是先进行右单旋再进行左单旋,操作是一样的,那么我们就可以进行代码复用,使用前面的左右单旋代码,这里比较麻烦的是平衡因子的更新~

              根据经验,我们可以利用结点插入后subRL的平衡因子来进行分情况讨论更新平衡因子~

      代码实现:

      void RotateRL(Node* parent)
      {
      	Node* subR = parent->_right;
      	Node* subRL = subR->_left;
      	//根据没有旋转前subRL的平衡因子分情况讨论
      	int bf = subRL->_bf;
      	//先以subR为旋转点右旋
      	RotateR(subR);
      	//再以parent为旋转点左旋
      	RotateL(parent);
      	//每一个结点父结点已经更新,只需要修改平衡因子
      	if (bf == -1)//插入在e子树
      	{
      		subRL->_bf = 0;
      		subR->_bf = 1;
      		parent->_bf = 0;
      	}
      	else if (bf == 1)//插入在f子树
      	{
      		subRL->_bf = 0;
      		subR->_bf = 0;
      		parent->_bf = -1;
      	}
      	else if (bf == 0)//插入在b子树自己
      	{
      		subRL->_bf = 0;
      		subR->_bf = 0;
      		parent->_bf = 0;
      	}
      	else//其他情况,说明有问题
      	{
      		assert(false);
      	}
      }

      AVL树的查找

              我们知道AVL树是二叉平衡搜索树,那么我们就可以使用二叉搜索树的查找逻辑~

      1. 初始化:从根节点开始遍历
      2. 循环比较:
        • 条件:只要当前节点不为空且未找到目标值,继续循环。
        • 比较操作:
          • 若目标值等于当前节点值,查找成功,返回当前节点。
          • 若目标值小于当前节点值,移动到左子节点。
          • 若目标值大于当前节点值,移动到右子节点。
          • 终止条件:
            • 找到目标节点,返回结果。
            • 当前节点为空,说明目标值不存在。

      代码实现:

      //查找结点
      Node* Find(const K&key)//查找key
      {
      	Node* cur = _root;
      	while (cur)
      	{
      		if (cur->_kv.first > key)//key在左子树
      		{
      			cur = cur->_left;
      		}
      		else if (cur->_kv.first _right;
      		}
      		else
      		{
      			return cur;//相等,返回当前结点
      		}
      	}
      	//没有找到返回空
      	return nullptr;
      }

      AVL树的实现就结束了~

      总代码

      #include
      #include
      #include
      using namespace std;
      //AVL树结点结构
      template
      struct AVLTreeNode
      {
      	pair _kv;//保存的数据类型
      	AVLTreeNode* _left;//左孩子结点指针
      	AVLTreeNode* _right;//右孩子结点指针
      	AVLTreeNode* _parent;//双亲结点指针
      	int _bf;//平衡因子
      	//初始化
      	AVLTreeNode(const pair& kv)
      		:_kv(kv),
      		_left(nullptr),
      		_right(nullptr),
      		_parent(nullptr),
      		_bf(0) 
      	{
      	}
      };
      //AVL树结构
      template
      class AVLTree
      {
      	typedef AVLTreeNode Node;
      private:
      	Node* _root = nullptr;//根节点
      public:
      	//功能实现
      	//查找结点
      	Node* Find(const K&key)//查找key
      	{
      		Node* cur = _root;
      		while (cur)
      		{
      			if (cur->_kv.first > key)//key在左子树
      			{
      				cur = cur->_left;
      			}
      			else if (cur->_kv.first _right;
      			}
      			else
      			{
      				return cur;//相等,返回当前结点
      			}
      		}
      		//没有找到返回空
      		return nullptr;
      	}
      	//插入结点
      	bool insert(const pair& kv)
      	{
      		if (_root == nullptr)//不存在根结点
      		{
      			//插入结点就是根结点
      			_root = new Node(kv);
      			return true;//插入成功
      		}
      		//存在根结点——找到应该插入的位置
      		Node* parent = nullptr;
      		Node* cur = _root;
      		while (cur)
      		{
      			if (kv.first _kv.first)//比当前结点小,插入当前结点左子树
      			{
      				parent = cur;
      				cur = cur->_left;
      			}
      			else if (kv.first > cur->_kv.first)//比当前结点大,插入当前结点右子树
      			{
      				parent = cur;
      				cur = cur->_right;
      			}
      			else//相等就不进行插入,插入失败
      			{
      				return false;
      			}
      		}
      		//parent即为插入结点父结点
      		cur = new Node(kv);
      		//判断是插入父结点左边还是右边
      		if (kv.first _kv.first)
      		{
      			parent->_left = cur;
      		}
      		else if (kv.first > parent->_kv.first)
      		{
      			parent->_right = cur;
      		}
      		cur->_parent = parent;
      		//平衡因子更新
      		while (parent)
      		{
      			//我们实现的AVL树平衡因子的计算公式为:平衡因子 = 右子树高度 - 左子树高度
      			if (parent->_left == cur)
      				//插入结点在左子树,父结点平衡因子--
      			{
      				parent->_bf--;
      			}
      			else if (parent->_right == cur)
      				//插入结点在右子树,父结点平衡因子++
      			{
      				parent->_bf++;
      			}
      			//分情况讨论
      			//1、父结点平衡因子更新为0,不需要继续向上更新
      			if (parent->_bf == 0)
      				break;
      			//2、父结点平衡因子更新为-1或者1,需要继续向上更新
      			//如果当前父结点为根结点,parent会到空,跳出循环
      			else if (parent->_bf == -1 || parent->_bf == 1)
      			{
      				cur = parent;
      				parent = parent->_parent;
      			}
      			//3、父结点平衡因子更新为-2或者2,已经不平衡需要旋转处理
      			else if (parent->_bf == -2 || parent->_bf == 2)
      			{
      				//旋转处理
      				//插入在右子树(右边高)——左单旋
      				if (parent->_bf == 2 && cur->_bf == 1)
      				{
      					RotateL(parent);
      				}
      				插入在左子树(左边高)——左单旋
      				else if (parent->_bf == -2 && cur->_bf == -1)
      				{
      					RotateR(parent);
      				}
      				//parent与cur平衡因子异号需要进行双旋
      				//左右双旋
      				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 RotateL(Node* parent)
      	{
      		Node* subR = parent->_right;//cur
      		Node* subRL = subR->_left;//B
      		//更改指向
      		subR->_left = parent;
      		parent->_right = subRL;
      		//修改变动结点(subR,subRL,parent)的parent
      		if(subRL)//subRL可能为空
      			subRL->_parent = parent;
      		parent->_parent = subR;
      		//subR可能成为整棵树的根结点,需要进行判断更新subR的parent
      		Node* parentParent = parent->_parent;
      		if (parentParent == nullptr)
      		{
      			//subR成为整棵树的根结点
      			_root = subR;
      			subR->_parent = nullptr;
      		}
      		else
      		{
      			if (parentParent->_left == parent)//判断原节点是父亲的左/右结点
      			{
      				parentParent->_left == subR;
      			}
      			else
      			{
      				parentParent->_right = subR;
      			}
      			//更新subR父结点
      			subR->_parent = parentParent;
      		}
      		//更新平衡因子
      		parent->_bf = subR->_bf = 0;
      	}
      	void RotateR(Node* parent)
      	{
      		Node* subL = parent->_left;
      		Node* subLR = subL->_right;//b
      		//旋转操作
      		parent->_left = subLR;
      		subL->_right = parent;
      		//更新相应结点parent
      		if (subLR)//h可能等于0,那么subLR可能为空
      			subLR->_parent = parent;
      		
      		Node* parentParent = parent->_parent;//保存父结点的父亲
      		if (parentParent == nullptr)//说明原来的父结点是根结点
      		{
      			_root = subL;
      			subL->_parent = nullptr;
      		}
      		else
      		{
      			if (parentParent->_left == parent)//父结点是左孩子
      			{
      				parentParent->_left = subL;
      			}
      			else
      			{
      				//父结点是右孩子
      				parentParent->_right = subL;
      			}
      			subL->_parent = parentParent;
      		}
      		parent->_parent = subL;
      		//更新平衡因子
      		subL->_bf = parent->_bf = 0;
      	}
      	void RotateLR(Node* parent)
      	{
      		Node* subL = parent->_left;
      		Node* subLR = subL->_right;
      		//根据没有旋转前subLR的平衡因子分情况讨论
      		int bf = subLR->_bf;
      		
      		//先以subL为旋转点左旋
      		RotateL(subL);
      		//再以parent为旋转点右旋
      		RotateR(parent);
      		//每一个结点父结点已经更新,只需要修改平衡因子
      		if (bf == -1)//插入在e子树
      		{
      			subLR->_bf = 0;
      			subL->_bf = 0;
      			parent->_bf = 1;
      		}
      		else if (bf == 1)//插入在f子树
      		{
      			subLR->_bf = 0;
      			subL->_bf = -1;
      			parent->_bf = 0;
      		}
      		else if (bf == 0)//插入在b子树自己
      		{
      			subLR->_bf = 0;
      			subL->_bf = 0;
      			parent->_bf = 0;
      		}
      		else//其他情况,说明有问题
      		{
      			assert(false);
      		}
      	}
      	void RotateRL(Node* parent)
      	{
      		Node* subR = parent->_right;
      		Node* subRL = subR->_left;
      		//根据没有旋转前subRL的平衡因子分情况讨论
      		int bf = subRL->_bf;
      		//先以subR为旋转点右旋
      		RotateR(subR);
      		//再以parent为旋转点左旋
      		RotateL(parent);
      		//每一个结点父结点已经更新,只需要修改平衡因子
      		if (bf == -1)//插入在e子树
      		{
      			subRL->_bf = 0;
      			subR->_bf = 1;
      			parent->_bf = 0;
      		}
      		else if (bf == 1)//插入在f子树
      		{
      			subRL->_bf = 0;
      			subR->_bf = 0;
      			parent->_bf = -1;
      		}
      		else if (bf == 0)//插入在b子树自己
      		{
      			subRL->_bf = 0;
      			subR->_bf = 0;
      			parent->_bf = 0;
      		}
      		else//其他情况,说明有问题
      		{
      			assert(false);
      		}
      	}
      };
      

      ♥♥♥本篇博客内容结束,期待与各位优秀程序员交流,有什么问题请私信♥♥♥

      ♥♥♥如果这一篇博客对你有帮助~别忘了点赞分享哦~♥♥♥

      ✨✨✨✨✨✨个人主页✨✨✨✨✨✨


免责声明:我们致力于保护作者版权,注重分享,被刊用文章因无法核实真实出处,未能及时与作者取得联系,或有版权异议的,请联系管理员,我们会立即处理! 部分文章是来自自研大数据AI进行生成,内容摘自(百度百科,百度知道,头条百科,中国民法典,刑法,牛津词典,新华词典,汉语词典,国家院校,科普平台)等数据,内容仅供学习参考,不准确地方联系删除处理! 图片声明:本站部分配图来自人工智能系统AI生成,觅知网授权图片,PxHere摄影无版权图库和百度,360,搜狗等多加搜索引擎自动关键词搜索配图,如有侵权的图片,请第一时间联系我们。

相关阅读

目录[+]

取消
微信二维码
微信二维码
支付宝二维码