平衡二叉树(AVL树)的插入讲解

本文详细介绍了AVL树的插入过程,包括遵循二叉搜索树规则插入节点、更新父节点平衡因子,以及根据平衡因子判断是否需要进行旋转操作。着重讨论了四种旋转类型:单旋和双旋,以确保树的平衡性。
摘要由CSDN通过智能技术生成

一.平衡二差树的概念

平衡二叉树的定义:对于平衡二叉树中的每个节点其左右子树高度之差(简称平衡因子)的绝对值不超过1(-1/0/1)。在这里我们为了讲解方便把平衡因子定为:右树最大高度减去左树最大高度。

节点的定义如下:

struct AVLTreeNode
{
	AVLTreeNode<K, V>* _left;
	AVLTreeNode<K, V>* _right;
	AVLTreeNode<K, V>* _parent;
	int _bf; // balance factor
	pair<K, V> _kv;

	AVLTreeNode(const pair<K, V>& kv)
		:_left(nullptr)
		, _right(nullptr)
		, _parent(nullptr)
		, _bf(0)
		, _kv(kv)
	{}
};

二.插入步骤

步骤如下:

1.插入:先按照二叉搜索树的规则将节点插入到AVL树中

2.更新父节点的平衡因子:新节点插入后,AVL树的平衡性可能会遭到破坏,此时就需要更新平衡因子,如果pCur插入到pParent的左侧,只需给pParent的平衡因子-1,如果pCur插入到pParent的右侧,只需给pParent的平衡因子+1。

3.根据父节点的平衡因子判定:

a.如果Parent的平衡因子为0,说明插入之前Parent的平衡因子为正负1,插入后被调整成0,此时插入没有改变高度,满足,不用做任何处理。

b.如果pParent的平衡因子为正负1,说明插入前pParent的平衡因子一定为0,插入后被更新成正负1,改变了树的高度,以pParent为根的树的高度增加,需要将pParent看成新插入的节点,重新从步骤二开始。如下图所示:需要将parent看成新插入的节点,跳转到步骤2,更新grandfather节点的平衡因子。

c.如果Parent的平衡因子为正负2,则Parent的平衡因子违反平衡树的性质,需要对其进行旋转处理。就如上图所示,此时已经不是平衡二叉树,需要进行处理。

该步骤用代码表示如下:

bool Insert(const pair<K, V>& kv)
//1.找到插入位置并插入
	{
		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)
		{
//2.更新平衡因子
			if (cur == parent->_left)
			{
				parent->_bf--;
			}
			else
			{
				parent->_bf++;
			}

//3.根据平衡因子判断


    //bf=0,不用处理跳出循环
			if (parent->_bf == 0)
			{
				break;
			}

    //bf=1或-1,需要更新节点
			else if (parent->_bf == 1 || parent->_bf == -1)
			{
				cur = cur->_parent;
				parent = parent->_parent;
			}

    //bf=2或-2,需要选择选转
			else if (parent->_bf == 2 || parent->_bf == -2)
			{
				//选转操作
		    }
            else
            {
                assert(false)//插入前就出错了
            }
	
		return true;
	}

三.旋转操作

我们首先要明白刚插入节点时,只看父子节点那俩层并不会出现旋转的情况,在我们不断向上更新时才会根据不同情况来决定要不要旋转。更新节点后要进行的旋转主要分为四种类型。

第一二种情况是单旋,后俩种是双旋。 

代码如下:

          else if (parent->_bf == 2 || parent->_bf == -2)
			{
				if (parent->_bf == 2 && cur->_bf == 1)
				{
					/*	p
				h     c
					 h  h
						  o*/
					leftrevolve(parent);
					break;
				}
				else if (parent->_bf == -2 && cur->_bf == -1)
				{
					/*	p
					   c   h
					 h  h
					o         */
					rightrevolve(parent);
					break;
				}
				else if (parent->_bf == 2 && cur->_bf == -1)
				{
					
					Node* rightch = parent->_right;
					Node* rlch = rightch->_left;
					int rl_bf = rlch->_bf;
					rlrevolve(parent);
					/*	p
					   h   c
						  h  h
						   o         */
					if (rl_bf == 1)
					{
						
						parent->_bf = -1;
						rightch->_bf = 0;
						rlch->_bf = 0;

					}


					/*	p
					   h   c
						  h  h
						 o         */
					else if (rl_bf == -1)
					{
						parent->_bf = 0;
						rightch->_bf = 1;
						rlch->_bf = 0;

					}
					/*	p
					      c
						 o         */
					else if (rl_bf == 0)
					{
						parent->_bf = 0;
						rightch->_bf = 0;
						rlch->_bf = 0;
					}
					else
					{
						assert(false);
					}
				}
				else if (parent->_bf == -2 && cur->_bf == 1)
				{
					/*	p
				   c   h
				 h  h
					o         */
					Node* leftch = parent->_left;
					Node* lrch = leftch->_right;
					lr_bf = lrch->_bf;
					
					lrrevolve(parent);
					if (lr_bf == 1)
					{
						leftch->_bf = -1;
						parent->_bf = 0;
						lrch->_bf = 0;
					}
					else if (lr_bf == -1)
					{
						parent->_bf = 1;
						leftch->_bf = 0;
						lrch->_bf = 0;

					}
					else if (lr_bf == 0)
					{
						parent->_bf = 0;
						leftch->_bf = 0;
						lrch->_bf = 0;
					}

				}
				else
				{
					assert(false);
				}
        }
    void leftrevolve(Node* parent)
	{
		

		Node* rightch = parent->_right;
		Node* pp = parent->_parent;
		Node* rlch = parent->_right->_left;
		parent->_right= rlch;                
		rightch->_left=parent;
		parent->_parent = rightch;
		rightch->_parent = pp;
		
		if (rlch)
		{
			rlch->_parent = parent;
		}
		
		if (pp == nullptr)
		{
			_root = rightch;
			

		}
		
		else
		{
			if (pp->_right == parent)
			{
				pp->_right = rightch;

			}
			else if (pp->_left == parent)
			{
				pp->_left =rightch;
			}
		}	
		
		parent->_bf = 0;
		rightch->_bf = 0;
		
		return;

	}
	void rightrevolve(Node* parent)
	{
		Node* leftch = parent->_left;
		Node* pp = parent->_parent;
		Node* lrch = parent->_left->_right;
		leftch->_right=parent;
		parent->_left = lrch;
		parent->_parent = leftch;
		leftch->_parent = pp;
		if (lrch)
		{
			lrch->_parent = parent;
		}
		if (pp == nullptr)
		{
			_root = leftch;
			

		}
		else
		{
			if (pp->_right == parent)
			{
				pp->_right = leftch;

			}
			else if (pp->_left == parent)
			{
				pp->_left =leftch;
			}
		}
		parent->_bf = 0;
		leftch->_bf = 0;

		return;

	}
	void rlrevolve(Node* parent)
	{
		rightrevolve(parent->_right);
		leftrevolve(parent);
	}
	void lrrevolve(Node* parent)
	{
		leftrevolve(parent->_left);
		rightrevolve(parent);

	}

  • 6
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值