C++——AVL树

1、AVL树的概念

2、AVL树的实现


1、AVL树的概念

AVL树又名平衡二叉树,实际上也是一颗二叉搜索树,只不过需要满足特定的条件:就是要保证左右子树的高度之差(简称为平衡因子)的绝对值不超过1。它搜索的时间复杂度为O(log2n)。

如图就是一颗建议的AVL树,我们规定平衡因子的值为右子树高度减去左子树高度。


2、AVL树的实现

AVL树节点的定义:

template<class T>
struct AVLTreeNode
{
 AVLTreeNode(const T& data)
     : _pLeft(nullptr), _pRight(nullptr), _pParent(nullptr)
 , _data(data), _bf(0)
 {}
 AVLTreeNode<T>* _pLeft;   // 该节点的左孩子
 AVLTreeNode<T>* _pRight;  // 该节点的右孩子
 AVLTreeNode<T>* _pParent; // 该节点的双亲
 T _data;
 int _bf;                  // 该节点的平衡因子
};

AVL树的插入:

AVL树实际上是在二叉搜索树的基础上引入了平衡因子的概念,因此插入要做的可以分为两步:

1、按照二叉搜索树的方式插入

2、调节节点的平衡因子

bool Insert(const T& data)
{
   // 1. 先按照二叉搜索树的规则将节点插入到AVL树中
   // ...
   
   // 2. 新节点插入后,AVL树的平衡性可能会遭到破坏,此时就需要更新平衡因子,并检测是否破坏了AVL树 的平衡性

pCur插入后,pParent的平衡因子一定需要调整,在插入之前,pParent
的平衡因子分为三种情况:-1,0, 1, 分以下两种情况:
 1. 如果pCur插入到pParent的左侧,只需给pParent的平衡因子-1即可
 2. 如果pCur插入到pParent的右侧,只需给pParent的平衡因子+1即可

此时:pParent的平衡因子可能有三种情况:0,正负1, 正负2
 1. 如果pParent的平衡因子为0,说明插入之前pParent的平衡因子为正负1,插入后被调整
成0,此时满足  AVL树的性质,插入成功
 2. 如果pParent的平衡因子为正负1,说明插入前pParent的平衡因子一定为0,插入后被更
新成正负1,此时以pParent为根的树的高度增加,需要继续向上更新
 3. 如果pParent的平衡因子为正负2,则pParent的平衡因子违反平衡树的性质,需要对其进
行旋转处理
*/
while (pParent)
{
       // 更新双亲的平衡因子
if (pCur == pParent->_pLeft)
pParent->_bf--;
else
pParent->_bf++;
// 更新后检测双亲的平衡因子
if (0 == pParent->_bf)
      {    
           break;
      }
else if (1 == pParent->_bf || -1 == pParent->_bf)
{
             // 插入前双亲的平衡因子是0,插入后双亲的平衡因为为1 或者 -1 ,说明以双亲
为根的二叉树
             // 的高度增加了一层,因此需要继续向上调整
pCur = pParent;
pParent = pCur->_pParent;
}
else
{
// 双亲的平衡因子为正负2,违反了AVL树的平衡性,需要对以pParent
// 为根的树进行旋转处理


if(2 == pParent->_bf)
            {
                 // ...
            }
             else
            {
                 // ...
            }
}
}
return true;
}

这里的旋转操作需要拿出来单独解析,根据节点插入位置的不同,AVL树的旋转总共分为四种:
1、节点插入较高左子树的左侧(左左,右单旋)
此时也需考虑一些情况:
如果30节点有右孩子,那么就需要将它的右孩子放在60节点的左孩子处(因为30节点的右孩子一定比30大但比60小)
也要考虑到60是根节点还是子树的情况,需要更新根节点或者记录60节点的双亲
void _RotateR(PNode pParent)
{
    // pSubL: pParent的左孩子
    // pSubLR: pParent左孩子的右孩子
 PNode pSubL = pParent->_pLeft;
 PNode pSubLR = pSubL->_pRight;
    // 旋转完成之后,30的右孩子作为双亲的左孩子
 pParent->_pLeft = pSubLR;
    // 如果30的左孩子的右孩子存在,更新亲双亲
 if(pSubLR)
 pSubLR->_pParent = pParent;
    // 60 作为 30的右孩子
 pSubL->_pRight = pParent;
    
    // 因为60可能是棵子树,因此在更新其双亲前必须先保存60的双亲
 PNode pPParent = pParent->_pParent;
    
    // 更新60的双亲
 pParent->_pParent = pSubL;
    
    // 更新30的双亲
 pSubL->_pParent = pPParent;
    // 如果60是根节点,根新指向根节点的指针
 if(NULL == pPParent)
 {
 _pRoot = pSubL;
 pSubL->_pParent = NULL;
 }
 else
 {
         // 如果60是子树,可能是其双亲的左子树,也可能是右子树
 if(pPParent->_pLeft == pParent)
 pPParent->_pLeft = pSubL;
 else
 pPParent->_pRight = pSubL;
 }
    // 根据调整后的结构更新部分节点的平衡因子
 pParent->_bf = pSubL->_bf = 0;
}

2、节点插入较高右子树的右侧(右右,左单旋)

这种情况实际就是上一种情况的类比,换了个方向:

需要注意的事项与实现也与上一种情况相同。
3、节点插入较高左子树的右侧(左右,先左单旋再右单旋)
// 旋转之前,60的平衡因子可能是-1/0/1,旋转完成之后,根据情况对其他节点的平衡因子进
行调整
void _RotateLR(PNode pParent)
{
 PNode pSubL = pParent->_pLeft;
 PNode pSubLR = pSubL->_pRight;
    
    // 旋转之前,保存pSubLR的平衡因子,旋转完成之后,需要根据该平衡因子来调整其他节
点的平衡因子
 int bf = pSubLR->_bf;
    
    // 先对30进行左单旋
 _RotateL(pParent->_pLeft);
    
    // 再对90进行右单旋
 _RotateR(pParent);
 if(1 == bf)
 pSubL->_bf = -1;
 else if(-1 == bf)
 pParent->_bf = 1;
}

4、节点插入较高右子树的左侧(右左,先右单旋再左单旋)

完整代码实现:

#include<iostream>
#include<assert.h>
using namespace std;
#include <vector>

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

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

template<class K, class V>
class AVLTree
{
	typedef AVLTreeNode<K, V> Node;
public:
	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);
		if (parent->_kv.first < kv.first)
		{
			parent->_right = cur;
		}
		else
		{
			parent->_left = cur;
		}

		cur->_parent = parent;

		// ... 控制平衡
		// 更新平衡因子
		while (parent)
		{
			if (cur == parent->_left)
			{
				parent->_bf--;
			}
			else // if (cur == parent->_right)
			{
				parent->_bf++;
			}

			if (parent->_bf == 0)
			{
				// 更新结束
				break;
			}
			else if (parent->_bf == 1 || parent->_bf == -1)
			{
				// 继续往上更新
				cur = parent;
				parent = parent->_parent;
			}
			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);
				}
				else if (parent->_bf == 2 && cur->_bf == -1)
				{
					RotateRL(parent);
				}
				else if (parent->_bf == -2 && cur->_bf == 1)
				{
					RotateLR(parent);
				}

				break;
			}
			else
			{
				assert(false);
			}
		}


		return true;
	}

	void RotateL(Node* parent)
	{
		++_rotateCount;

		Node* cur = parent->_right;
		Node* curleft = cur->_left;

		parent->_right = curleft;
		if (curleft)
		{
			curleft->_parent = parent;
		}

		cur->_left = parent;

		Node* ppnode = parent->_parent;

		parent->_parent = cur;


		if (parent == _root)
		{
			_root = cur;
			cur->_parent = nullptr;
		}
		else
		{
			if (ppnode->_left == parent)
			{
				ppnode->_left = cur;
			}
			else
			{
				ppnode->_right = cur;

			}

			cur->_parent = ppnode;
		}

		parent->_bf = cur->_bf = 0;
	}


	void RotateR(Node* parent)
	{
		++_rotateCount;

		Node* cur = parent->_left;
		Node* curright = cur->_right;

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

		Node* ppnode = parent->_parent;
		cur->_right = parent;
		parent->_parent = cur;

		if (ppnode == nullptr)
		{
			_root = cur;
			cur->_parent = nullptr;
		}
		else
		{
			if (ppnode->_left == parent)
			{
				ppnode->_left = cur;
			}
			else
			{
				ppnode->_right = cur;
			}

			cur->_parent = ppnode;
		}

		parent->_bf = cur->_bf = 0;
	}

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

		RotateR(parent->_right);
		RotateL(parent);

		if (bf == 0)
		{
			cur->_bf = 0;
			curleft->_bf = 0;
			parent->_bf = 0;
		}
		else if (bf == 1)
		{
			cur->_bf = 0;
			curleft->_bf = 0;
			parent->_bf = -1;
		}
		else if (bf == -1)
		{
			cur->_bf = 1;
			curleft->_bf = 0;
			parent->_bf = 0;
		}
		else
		{
			assert(false);
		}
	}

	void RotateLR(Node* parent)
	{
		Node* cur = parent->_left;
		Node* curright = cur->_right;
		int bf = curright->_bf;

		RotateL(parent->_left);
		RotateR(parent);

		if (bf == 0)
		{
			parent->_bf = 0;
			cur->_bf = 0;
			curright->_bf = 0;
		}
		else if (bf == -1)
		{
			parent->_bf = 1;
			cur->_bf = 0;
			curright->_bf = 0;
		}
		else if (bf == 1)
		{
			parent->_bf = 0;
			cur->_bf = -1;
			curright->_bf = 0;
		}
	}

	int Height()
	{
		return Height(_root);
	}

	int Height(Node* root)
	{
		if (root == nullptr)
			return 0;

		int leftHeight = Height(root->_left);
		int rightHeight = Height(root->_right);

		return leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;
	}

	bool IsBalance()
	{
		return IsBalance(_root);
	}

	bool IsBalance(Node* root)
	{
		if (root == nullptr)
			return true;

		int leftHight = Height(root->_left);
		int rightHight = Height(root->_right);

		if (rightHight - leftHight != root->_bf)
		{
			cout << "平衡因子异常:" << root->_kv.first << "->" << root->_bf << endl;
			return false;
		}

		return abs(rightHight - leftHight) < 2
			&& IsBalance(root->_left)
			&& IsBalance(root->_right);
	}

private:
	Node* _root = nullptr;

public:
	int _rotateCount = 0;
};

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

双葉Souyou

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

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

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

打赏作者

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

抵扣说明:

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

余额充值