AVL树的旋转

目录

结点的定义

左旋

右旋:

右左双旋:

左右双旋:


二叉搜索树虽可以缩短查找的效率,但如果数据有序或接近有序二叉搜索树时,二叉搜索树的效率就会很低。因此出现了AVL树,AVL树是一种高度平衡的二叉搜索树,AVL树的每个结点的左右子树高度差的绝对值不超出1,是通过旋转的方式来维护这颗树的结构

结点的定义

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)
	{}
};

这里引用了平衡因子的概念,平衡因子是通过右树减去左树来得到的,当一个树的平衡因子的绝对值大于等于2是,便会不平衡,这时就需要通过旋转的方式将树重新平衡起来。

左旋

 当我们插入第三个结点时,树就会变得不平衡,我们就需要旋转处理。

当parent的_bf等于2,childR的_bf是1时,我们左旋转

我们需要注意:这里的parent不一定根节点,也可能是一颗子树 ,我们需要定义一个指针,指向parent的_parent;

  

代码实现:

    void RotateL(Node* parent)
	{
		Node* ChildR = parent->_right;
		Node* ChildRL = ChildR->_left;
		Node* parentParent = parent->_parent;
        
        链接childRL和parent
		parent->_right=childRL;  
        if(childRL)
          childRL->_parent=parent;

        链接childR和parent
       childR->_left=parent;
       parent->_parent=childR

        
       //判断parent是否是根节点
       if(_root==ParRar) {
         _root=childR;
         childR->_parent=nullptr;
       }
       
       else {
          if(parpar->_left=parent)
            parpar->_left=childR;
          else if(parpar->_right=parent)
            parpar->_right=childR;

          childR->_parent=parpar;
       }
       //调整平衡因子
       childR->_bf=parent->_bf=0;
    
	}

右旋:

和左旋类似

 且当parent的_bf等于-2,cildL的_bf是-1时,我们右旋转

 我们需要注意:这里的parent不一定根节点,也可能是一颗子树 ,我们需要定义一个指针,指向parent的_parent;

 代码实现:

void RotateR(Node* parent)
	{
		Node* ChildL = parent->_left;
		Node* ChildLR = subL->_right;
		Node* parpar = parent->_parent;
        
        //链接parent和 ChildRL
        parent->_right=ChildRL;
        if(Child)
          childRL->_parent=parent;
         
         //链接·parent和childR
         ChildR->_left=parent;
         parent->_parent=ChildR;
        
         if(_root==parent)
         {
           _root=ChildR;
           ChildR->_parent=nullptr;
         }

        else
        {
           if(parpar->_left=parent)
              parpatr->_left=ChildR;
           else if(parpar->_right=parent)
              parpar->_right=ChildR;

          ChildR->_parent=parpar;
        }
       
        parent->_bf=ChildR->_bf=0;  

	}

右左双旋:

当parent的平衡因子为2,childR的平衡因子-1是,都构成右左双旋。

我们需要先以chidlR进行一次右旋,在_parent进行一次左旋

注意:在childRL的左右子树插入时,对平衡因子的处理不同,所以要分类讨论,我们定义一个变量bf来判断是在左子树或右子树,

当bf=-1是:

 

 

当bf=1时:

 

代码实现:

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

		//1、以childR为轴进行右单旋
		RotateR(childR);

		//2、以parent为轴进行左单旋
		RotateL(parent);

		//3、更新平衡因子
		if (bf == 1)
		{
			childRL->_bf = 0;
			parent->_bf = -1;
			childR->_bf = 0;
		}
		else if (bf == -1)
		{
			childRL->_bf = 0;
			parent->_bf = 0;
			childR->_bf = 1;
		}
		else if (bf == 0)
		{
			childRL->_bf = 0;
			parent->_bf = 0;
			childR->_bf = 0;
		}
	}

左右双旋:

当parent的平衡因子为-2,childL的平衡因子1是,就构成左右双旋。

 

我们需要先以chidlL进行一次左旋,在_parent进行一次右旋

注意:在childLR的左右子树插入时,对平衡因子的处理不同,所以要分类讨论,我们定义一个变量bf来判断是在左子树或右子树,

当bf=-1时:

 

当bf=1时:

 

代码实现:

void RotateLR(Node* parent)
	{
		Node* childL = parent->_left;
		Node* childLR = subL->_right;
		int bf = childLR->_bf; //subLR不可能为nullptr,因为subL的平衡因子是1

		//1、以subL为旋转点进行左单旋
		RotateL(childL);

		//2、以parent为旋转点进行右单旋
		RotateR(parent);

		//3、更新平衡因子
		if (bf == 1)
		{
			childLR->_bf = 0;
			childL->_bf = -1;
			parent->_bf = 0;
		}
		else if (bf == -1)
		{
			childLR->_bf = 0;
			childL->_bf = 0;
			parent->_bf = 1;
		}
		else if (bf == 0)
		{
			childLR->_bf = 0;
			childL->_bf = 0;
			parent->_bf = 0;
		}
	}

以上便是旋转的四种方式

章尚有不足,欢迎大牛指正

如果本文对您有帮助请点赞支持一下~

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值