【高级程序设计语言C++】AVL树

1. AVL树的概念

AVL树是一种自平衡二叉搜索树,它在每次插入或删除节点时自动调整以保持树的平衡。AVL树的平衡是通过节点的高度差来衡量的,即左子树的高度和右子树的高度之差不超过1。

AVL树的特点如下:

  • 每个节点都有一个平衡因子,定义为左子树的高度减去右子树的高度。平衡因子的值只能是-1、0或1。
  • AVL树中的任何节点的左子树和右子树都是AVL树。
  • 对于AVL树中的每个节点,左子树的高度和右子树的高度之差不超过1。

当向AVL树中插入新节点或删除节点时,AVL树会根据节点的平衡因子进行自动调整。如果插入或删除导致某个节点的平衡因子超过了允许的范围(-1、0或1),则需要通过旋转操作来重新平衡树。

2. AVL树的旋转

AVL树的旋转操作包括左旋和右旋。左旋是指将一个节点的右子树提升为其父节点,并将父节点降为其左子节点。右旋是指将一个节点的左子树提升为其父节点,并将父节点降为其右子节点。通过旋转操作,AVL树可以保持平衡。

2.1. 左单旋

左单旋的情况简图如下:

img

需要左单旋,那么就是右边的分支出现了平衡因子异常,他的形象图类似一条直线。

img

总体来说就是右边高,需要向左旋转,依据的是平衡因子来确定是否需要旋转。

当父亲的平衡因子为2,子树的平衡因子为1的时候,就需要左单旋。旋转完成后,需要注意平衡因子的更新。

// 左单旋
void RotateL(Node* pParent)
{
    Node* subR = pParent->_pRight;
    Node* subRL = subR->_pLeft;

    pParent->_pRight = subRL;
    if (subRL)
    {
        subRL->_pParent = pParent;
    }

    Node* pPParent = pParent->_pParent;
    subR->_pLeft = pParent;
    pParent->_pParent = subR;
    if (pPParent)
    {
        if (pParent == pPParent->_pLeft)
        {
            pPParent->_pLeft = subR;
        }
        else if (pParent == pPParent->_pRight)
        {
            pPParent->_pRight = subR;
        }
        subR->_pParent = pPParent;
    }
    else
    {
        _root = subR;
        _root->_pParent = nullptr;
    }
    subR->_bf = pParent->_bf = 0;
}

旋转之后的树状图如下:

img

2.2 右单旋

img

具体的图像请读者自行画出。

总体来说就是,左边高,就需要往右边旋转,依据平衡因子来决定。

当父亲的平衡因子为-2,子树的平衡因子为-1时就需要右单旋。旋转完成后需要注意平衡因子的更新。

// 右单旋
void RotateR(Node* pParent)
{
    Node* subL = pParent->_pLeft;
    Node* subLR = subL->_pRight;
    pParent->_pLeft = subLR;
    if (subLR)
    {
        subLR->_pParent = pParent;
    }
    Node* pPParent = pParent->_pParent;
    subL->_pRight = pParent;
    pParent->_pParent = subL;
    if (pPParent)
    {
        if (pPParent->_pLeft == pParent)
        {
            pPParent->_pLeft = subL;
        }
        else if (pPParent->_pRight == pParent)
        {
            pPParent->_pRight = subL;
        }
        subL->_pParent = pPParent;
    }
    else
    {
        _root = subL;
        _root->_pParent = nullptr;
    }
    pParent->_bf = subL->_bf = 0;
}

2.3 左右双旋

img

具体来说就是,先进行左单旋,捋直后进行右单旋。依据平衡因子来决定。

当父亲的平衡因子为-2,子树的平衡因子为1时,就要进行左右双旋。

void RotateLR(Node* pParent)
	{
		Node* subL = pParent->_pLeft;
		Node* subLR = subL->_pRight;
		int bf = subLR->_bf;
		RotateL(subL);
		RotateR(pParent);
		if (bf == 0)
		{
			subL->_bf = 0;
			pParent->_bf = 0;
			subLR->_bf = 0;
		}
		else if (bf == -1)
		{
			subL->_bf = 0;
			pParent->_bf = 1;
			subLR->_bf = 0;
		}
		else if (bf == 1)
		{
			subL->_bf = -1;
			pParent->_bf = 0;
			subLR->_bf = 0;
		}
		else
		{
			assert(false);
		}
	}

2.4 右左双旋

img

参考左右双旋,根据上图自行绘出节点的走势。

右左双旋的依据也是根据平衡因子来决定的,当父亲的平衡因子为2,子树的平衡因子为-1时,即要进行右左双旋。

// 右左双旋
void RotateRL(Node* pParent)
{
    Node* subR = pParent->_pRight;
    Node* subRL = subR->_pLeft;
    int bf = subRL->_bf;
    RotateR(subR);
    RotateL(pParent);
    if (bf == -1)
    {
        subRL->_bf = 0;
        pParent->_bf = 0;
        subR->_bf = 1;
    }
    else if (bf == 0)
    {
        subRL->_bf = 0;
        pParent->_bf = 0;
        subR->_bf = 0;
    }
    else if (bf == 1)
    {
        subRL->_bf = 0;
        pParent->_bf = -1;
        subR->_bf = 0;
    }
    else
    {
        assert(false);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值