第五章:AVL树

目录

1.AVL树介绍:

2.节点介绍(平衡因子):

 3.插入一个新节点,如何更新平衡因子?

4.插入的实现:

5.旋转调整AVL树:

5.1单左旋:

5.2单右旋:

5.3右+左旋转:

5.4左+右旋转:


1.AVL树介绍:

  • AVL树是一种自平衡二叉搜索树,它确保在进行插入和删除操作后,树的高度差(即平衡因子)不会超过1,从而维持了二叉搜索树的平衡。进而降低树的高度,以此来减少平均搜索长度。
  • AVL树的时间复杂度为log(n);

2.节点介绍(平衡因子):

  • AVL树一般包含左右子树指针,父节点指针,平衡因子,自身节点值。
  • 平衡因子计算公式为:balanceFactor(N)=height(N.left)−height(N.right)。
  • 新建的节点的平衡因子为0;
  • 如果平衡因子为0,表示以当前节点为根节点的树的左右子树高度相同,节点是平衡的。
  • 如果平衡因子为1,表示左子树比右子树高一层,节点是平衡的。
  • 如果平衡因子为-1,表示右子树比左子树高一层,节点是平衡的。
  • 如果平衡因子为2或-2,表示节点不平衡,需要通过旋转操作进行调整。

 3.插入一个新节点,如何更新平衡因子?

  • 按照二叉搜索树的规则将节点插入到AVL树中。可能会影响祖先,但是不会影响祖先的兄弟节点。如果新增的节点在左子树,高度差变小,父亲节点的平衡因子要--;如果新增的节点在右子树,高度差变大,父亲节点的平衡因子要++;

  • 在更新平衡因子之后,如果父亲节点的平衡因子变为0,就不用向上更新了,插入完成;如果父亲的平衡因子变为1或者-1,父亲两个子树产生高度差,要继续向上更新平衡因子。当向上更新后某个祖先节点的平衡因子变为2或者-2,就要旋转调整AVL树。

4.插入的实现:

  • AVL树要满足搜索二叉树性质,插入时要先从根节点向下找到合适的位置来插入新增节点。
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);
		cur->_parent = parent;
		if (parent->_kv.first < kv.first)
		{
			parent->_right = cur;
		}
		else
		{
			parent->_left = cur;
		}

		return true;
	}
}
  • 插入结点之后,开始更新平衡因子。更新之后根据新的平衡因子判断是否继续向上更新平衡因子。
  • 如果平衡因子为0,无需再向上更新平衡因子。AVL树已经平衡。
  • 如果平衡因子为 1/-1,继续向上更新平衡因子。
  • 如果平衡因子为 2/-2,找到了不平衡的子树的根节点,开始翻转调整到平衡。调整后树的高度降低,无需再向上继续调整平衡因子。
while (parent)//极限情况是更行到根
{
	if (cur == parent->_left)//插在左树,高度差减小,平衡因子要从大于0趋近于0,平衡因子减小
		parent->_bf--;
	else//插在右树,高度差增大,平衡因子增大
		parent->_bf++;

	if (parent->_bf == 0)//如果更新后的父亲的平衡因子变为0,停止向上更新平衡因子
		break;
	else if (parent->_bf == 1 || parent->_bf == -1)//如果平衡因子为1或-1,继续向上更新
	{
        cur = parent;
        parent = cur->_parent;
	}

	else if (parent->_bf == 2 || parent->_bf == 2)//平衡因子为2或者-2的情况,翻转
	{
	    //处理单左旋情况
		if (cur->_bf == 1 && parent->_bf == 2)
		{
			RoteL(parent);
		}

	    //处理单右旋情况
		else if (cur->_bf == -1 && parent->_bf == -2)
		{
			RoteR(parent);
		}
    
		//处理完旋转,就不需要再向上调整,直接跳出即可
		break;
	}

	//如果平衡因子出现-2,-1,0,1,2以外的数,说明出错了
	else
	{
		assert(false);
	}
}

5.旋转调整AVL树:

5.1单左旋:

  • 更新平衡因子之后,当parent的平衡因子为2,cur的平衡因子为1时,使用单左旋。

        

  • 如上图,将b节点调整到值为30的节点的右子树,将值为60的节点作为新的父节点,将值为30的节点作为值为60的节点的左子树节点。其他不用调整。
void _RotateL(Node Parent)
{
    Node* sub=parent->_right;
    Node* subL=sub->_left;

    parent->_right=subL;
    if(subL)
        subL->_parent=parent;

    Node* pp=parent->_parent;
    parent->_parent=sub;
    sub->_left=parent;
    
    if(_root==parent)
    {
        _root=sub;
        sub->_parent=nullptr;
    }
    else
    {
        if(pp->_left==parent)
            pp->_left=sub;
        else
            pp->_right=sub;
        sub->_parent=pp;
    }
    parent->_bf=sub->_bf=0;
}

5.2单右旋:

  • 更新平衡因子之后,当parent的平衡因子为-2,cur的平衡因子为-1时,使用单右旋。

        

void _RotateR(Node Parent)
{
    Node* sub=parent->_left;
    Node* subR=sub->_right;

    parent->_left=subR;
    if(subR)
        subR->_parent=parent;

    Node* pp=parent->_parent;
    parent->_parent=sub;
    sub->_right=parent;
    
    if(_root==parent)
    {
        _root=sub;
        sub->_parent=nullptr;
    }
    else
    {
        if(pp->_left==parent)
            pp->_left=sub;
        else
            pp->_right=sub;
        sub->_parent=pp;
    }
    parent->_bf=sub->_bf=0;
}

5.3右+左旋转:

  • 更新平衡因子之后,当parent的平衡因子为2,cur的平衡因子为-1时,在cur右旋,然后在parent左旋。
void RoteRL(Node* parent)
{
	Node* subR = parent->_right;
	Node* subRL = subR->_left;

	int bf = subRL->_bf;

	RoteR(parent->_right);//在cur节点右旋
	RoteL(parent);//在parent位置左旋

	//旋转完成,对三个节点的平衡因子进行更新
	if (bf == 0)
	{
		parent->_bf = subR->_bf = subRL->_bf = 0;
	}
	else if (bf == -1)
	{
		parent->_bf = 0;
		subRL->_bf = 0;
		subR->_bf = 1;
	}
	else if (bf == 1)
	{
		parent->_bf = -1;
		subR->_bf = 0;
		subRL->_bf = 0;
	}
	else
	{
		assert(false);
	}
}

5.4左+右旋转:

  • 更新平衡因子之后,当parent的平衡因子为-2,cur的平衡因子为1时,在cur左旋,然后在parent右旋。
void RoteLR(Node* parent)
{
	Node* subL = parent->_left;
	Node* subLR = subR->_right;

	int bf = subLR->_bf;

	RoteL(parent->_left);//在cur节点左旋
	RoteR(parent);//在parent位置右旋

	//旋转完成,对三个节点的平衡因子进行更新
	if (bf == 0)
	{
		parent->_bf = subL->_bf = subLR->_bf = 0;
	}
	else if (bf == -1)
	{
		parent->_bf = 1;
		subLR->_bf = 0;
		subL->_bf = 0;
	}
	else if (bf == 1)
	{
		parent->_bf = 0;
		subL->_bf = -1;
		subLR->_bf = 0;
	}
	else
	{
		assert(false);
	}
}

  • 6
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

S+叮当猫

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值