【C++】AVL树详解

目录

一、AVL树的概念

二、AVL树节点的定义

三、AVL树的操作

3.1 AVL树的平衡因子

3.2 AVL树的插入

3.3 AVL树的旋转

3.4 AVL树的验证

 四、AVL树的完整代码


上一篇已经对关联式容器set/map/multiset/multimap进行了简答的介绍,大家可能发现它们有一个共同点:其底层都是按照二叉搜索树来实现的,但是学习二叉搜索树时,已经知道当树中插入的元素有序或接近有序时,二叉搜索树的会变得极不平衡,查找操作的时间复杂度可能达到 O(n),甚至退化成链表。因此map、set等关联式容器的底层结构是对二叉树进行了平衡处理,即采用平衡树来实现。

一、AVL树的概念

AVL树是一种自平衡二叉搜索树,它得名于它的发明者 Adelson-Velsky 和 Landis。AVL树通过在每次插入或删除节点时进行旋转操作来保持树的平衡,以确保每个结点的左右子树高度之差的绝对值不超过1,降低树的高度,从而实现较高效率的查找、插入和删除操作。

AVL树的性质:

  • 它的左右子树都是AVL树
  • 每个节点的左子树和右子树的高度差(平衡因子)不超过1。
  • 如果插入或删除操作导致树失去平衡,AVL树会通过旋转操作(包括单旋转和双旋转)来重新平衡。
  • 若一个AVL树有n个节点,它的查找、插入和删除操作的时间复杂度都是 O(log_2 n),高度可保持在 O(log_2 n)。

二、AVL树节点的定义

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;//平衡因子balance factor

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

三、AVL树的操作

3.1 AVL树的平衡因子

AVL 树的平衡因子是指每个节点的左子树高度减去右子树高度的值,即左子树高度 - 右子树高度。对于任意一个节点,其平衡因子可以是 -1、0 或 1。

AVL 树的平衡因子定义如下:

  • 如果一个节点的平衡因子为 -1,表示该节点的右子树比左子树高度高 1;
  • 如果一个节点的平衡因子为 0,表示该节点的左子树和右子树高度相等;
  • 如果一个节点的平衡因子为 1,表示该节点的左子树比右子树高度高 1。

AVL 树通过保持每个节点的平衡因子为 -1、0 或 1 来确保树的平衡。当插入或删除节点后,需要通过旋转操作来调整各个节点的平衡因子,以确保整棵树仍保持平衡状态。

3.2 AVL树的插入

在 AVL 树中插入元素的过程如下:

  1. 按照二叉搜索树的规则,找到新元素应该插入的位置,将其作为叶子节点插入到树中
  2. 在插入新元素后,从插入点开始向上回溯,更新每个祖先节点的平衡因子,并检查它们是否失去了平衡。
  3. 如果发现某个节点失去了平衡,则需要对其进行旋转操作,以恢复整棵树的平衡。

步骤一:按照二叉搜索树的规则插入新元素

template<class K, class V>
class AVLTree
{
	typedef AVLTreeNode<K, V> Node;
public:
	bool Insert(const pair<K, V>& kv)
	{
		Node* pnode = new Node(kv);

		if (_root == nullptr)
		{
			_root = pnode;
			return true;
		}

		Node* parent = nullptr;
		Node* cur = _root;

		while (cur)
		{
			if (cur->_kv.first < pnode->_kv.first)
			{
				parent = cur;
				cur = cur->_right;
			}
			else if (cur->_kv.first > pnode->_kv.first)
			{
				parent = cur;
				cur = cur->_left;
			}
			else
			{
				return false;
			}
		}

		if (parent->_kv.first < pnode->_kv.first)
		{
			parent->_right = pnode;
			pnode->_parent = parent;
		}
		else
		{
			parent->_left = pnode;
			pnode->_parent = parent;
		}

		//开始处理平衡因子
        //...

private:
	Node* _root = nullptr;
};

步骤二:更新每个祖先节点的平衡因子,检查它们是否失去了平衡。

在插入元素之前,parent的平衡因子分为三种情况:0,1,-1,插入后分为以下两种情况:

  • 如果cur插入到parent的左侧,只需给parent的平衡因子-1即可
  • 如果cur插入到parent的右侧,只需给parent的平衡因子+1即可
bool Insert(const pair<K, V>& kv)
	{
        //.....
         
        //开始处理平衡因子
		cur = pnode;
		while (parent)
		{
			if (cur == parent->_left)
			{
				parent->_bf--;
			}
			else
			{
				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)
			{
			    //失去了平衡,需要进行旋转
                //旋转操作。。。
			}
			else
			{
				assert(false);
			}
		}
		return true;
	}

插入后,parent节点的平衡因子发生变化,有这么三种情况:0,正负1, 正负2。

  • 情况1:parent的平衡因子变为0(parent的平衡因子一定是由1或-1变成0
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值