C++ AVLTree 的实现

AVLTree前言:二叉搜索树虽可以缩短查找的效率,但如果数据有序或接近有序二叉搜索树将退化为单支树,查找元素相当于在顺序表中搜索元素,效率低下。因此,两位俄罗斯的数学家G.M.Adelson-Velskii和E.M.Landis在1962年
发明了一种解决上述问题的方法:当向二叉搜索树中插入新结点后,如果能保证每个结点的左右子树高度之差的绝对值不超过1(需要对树中的结点进行调整),即可降低树的高度,从而减少平均搜索长度。

目录:

  • AVLTree特性
  • AVLTree定义
  • AVLTree插入
    • 插入第一步
    • 插入第二步
  • AVLTree旋转过程
    • 单旋转的抽象图
    • 单旋旋转过程
    • 双旋转抽象图
    • 双旋转抽象过程
  • AVLTree实现

AVLTree特性

  • 一棵AVL树或者是空树,或者是具有以下性质的二叉搜索树:
  • 它的左右子树都是AVL树左右子树高度之差(简称平衡因子)的绝对值不超过1(-1/0/1)
    在这里插入图片描述

如果一棵二叉搜索树是高度平衡的,它就是AVL树。如果它有n个结点,其高度可保持在 O ( l o g 2 n ) O(log_2 n) O(log2n),搜索时间复杂度O( l o g 2 n log_2 n log2n)

AVLTree定义

在这里插入图片描述

template<class T>
struct AVLTreeNode
{
	AVLTreeNode(const T& data = T())
		: _pLeft(nullptr)
		, _pRight(nullptr)
		, _pParent(nullptr)
		, _data(data)
		, _bf(0)
	{}

	AVLTreeNode<T>* _pLeft;
	AVLTreeNode<T>* _pRight;
	AVLTreeNode<T>* _pParent;
	T _data;
	int _bf;   // 节点的平衡因子
};

AVLTree插入

AVL树就是在二叉搜索树的基础上引入了平衡因子,因此AVL树也可以看成是二叉搜索树。那么
AVL树的插入过程可以分为两步:

  1. 按照二叉搜索树的方式插入新节点
  2. 调整节点的平衡因子

第一步的过程:

按照二叉搜索树的方式插入新节点

在这里插入图片描述

请添加图片描述

第二步的过程:

调整节点的平衡因子
在这里插入图片描述
请添加图片描述

旋转过程

旋转有四种旋转:左单旋,右单旋,左右双旋,右左双旋

单旋转的抽象图

AVLTree的抽象图是抽象子树的高度
长方形代表所有平衡的子树

左单旋、右单旋抽象图

在这里插入图片描述

单旋旋转过程

代码实现:要注意的点

  1. grandparent是否为根
  2. curLeft是否为空
  3. 不仅要更新左右孩子指针,还有更新父亲指针
  4. 一共涉及四个结点的改动:
    1.grandparent的parent
    2.grandparent
    3.cur
    4.curLeft
    在这里插入图片描述

请添加图片描述

右单旋类似于左单旋
左右单旋实现代码如下

// 右单旋
	void RotateR(Node* pParent)
	{
		Node* grandparent = pParent->_pParent;
		Node* cur = pParent->_pLeft;
		Node* curRight = cur->_pRight;

		cur->_pRight = pParent;
		cur->_pParent = grandparent;

		pParent->_pLeft = curRight;
		pParent->_pParent = cur;

		if (curRight)
			curRight->_pParent = pParent;
		if (pParent == _pRoot)
		{
			_pRoot = cur;
		}
		else
		{
			if (grandparent->_pLeft == pParent)
			{
				grandparent->_pLeft = cur;
			}
			else
			{
				grandparent->_pRight = cur;
			}
		}
		pParent->_bf = cur->_bf = 0;
	}
	// 左单旋
	void RotateL(Node* pParent)
	{
		Node* grandparent = pParent->_pParent;
		Node* cur = pParent->_pRight;
		Node* curLeft = cur->_pLeft;

		cur->_pLeft = pParent;
		cur->_pParent = grandparent;

		pParent->_pRight = curLeft;
		pParent->_pParent = cur;

		if (curLeft)
			curLeft->_pParent = pParent;
		if (pParent == _pRoot)
		{
			_pRoot = cur;
		}
		else
		{
			if (grandparent->_pLeft == pParent)
			{
				grandparent->_pLeft = cur;
			}
			else
			{
				grandparent->_pRight = cur;
			}
		}
		pParent->_bf = cur->_bf = 0;
	}

双旋转抽象图

第一种情况
在这里插入图片描述

第二种情况
左边为左右双旋,右边为右左双旋
在这里插入图片描述

双旋旋转过程

在这里插入图片描述

请添加图片描述

右左单旋类似于左右单旋
双旋实现代码如下

// 右左双旋
	void RotateRL(Node* pParent)
	{
		Node* cur = pParent->_pRight;
		Node* curLeft = cur->_pLeft;

		if (curLeft->_bf == 1)
		{
			RotateR(cur);
			RotateL(pParent);
			curLeft->_bf = 0;
			pParent->_bf = -1;
			cur->_bf = 0;
		}
		else if (curLeft->_bf == -1)
		{
			RotateR(cur);
			RotateL(pParent);
			cur->_bf = 1;
			pParent->_bf = curLeft->_bf = 0;
		}
		else
		{
			RotateR(cur);
			RotateL(pParent);
			pParent->_bf = curLeft->_bf = cur->_bf = 0;
		}

	}
	// 左右双旋
	void RotateLR(Node* pParent)
	{
		Node* cur = pParent->_pLeft;
		Node* curRight = cur->_pRight;

		if (curRight->_bf == 1)
		{
			RotateL(cur);
			RotateR(pParent);
			cur->_bf = -1;
			curRight->_bf = pParent->_bf = 0;
		}
		else if (curRight->_bf == -1)
		{
			RotateL(cur);
			RotateR(pParent);
			cur->_bf = curRight->_bf = 0;
			pParent->_bf = 1;
		}
		else
		{
			RotateL(cur);
			RotateR(pParent);
			pParent->_bf = curRight->_bf = cur->_bf = 0;
		}
	}

AVLTree代码实现如下


template<class T>
struct AVLTreeNode
{
	AVLTreeNode(const T& data = T())
		: _pLeft(nullptr)
		, _pRight(nullptr)
		, _pParent(nullptr)
		, _data(data)
		, _bf(0)
	{}

	AVLTreeNode<T>* _pLeft;
	AVLTreeNode<T>* _pRight;
	AVLTreeNode<T>* _pParent;
	T _data;
	int _bf;   // 节点的平衡因子
};


// AVL: 二叉搜索树 + 平衡因子的限制
template<class T>
class AVLTree
{
	typedef AVLTreeNode<T> Node;
public:
	AVLTree()
		: _pRoot(nullptr)
	{}

	// 在AVL树中插入值为data的节点
	bool Insert(const T& data)
	{
		if (_pRoot == nullptr)
		{
			_pRoot = new Node(data);
			return true;
		}
		Node* cur = _pRoot;
		Node* parent = nullptr;
		while (cur)
		{
			if (cur->_data < data)
			{
				parent = cur;
				cur = cur->_pRight;
			}
			else if (cur->_data > data)
			{
				parent = cur;
				cur = cur->_pLeft;
			}
			else
			{
				return false;
			}
		}
		cur = new Node(data);
		if (parent->_data > cur->_data)
		{
			parent->_pLeft = cur;
		}
		else
		{
			parent->_pRight = cur;
		}
		cur->_pParent = parent;

		while (parent)
		{
			if (parent->_pRight == cur)
			{
				parent->_bf++;
			}
			else
			{
				parent->_bf--;
			}

			if (parent->_bf == 0)
			{
				break;
			}
			else if (parent->_bf == -1 || parent->_bf == 1)
			{
				cur = parent;
				parent = parent->_pParent;
			}
			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)
				{
					RotateRL(parent);
				}
				else if (parent->_bf == -2 && cur->_bf == -1)
				{
					RotateR(parent);
				}
				else if (parent->_bf == -2 && cur->_bf == 1)
				{
					RotateLR(parent);
				}
				break;
				// AVL树的验证
				bool IsAVLTree()
				{
					return _IsAVLTree(_pRoot);
				}
			}
			else
			{
				assert("违反平衡因子规则");
			}
		}

	}
	

private:
	// 根据AVL树的概念验证pRoot是否为有效的AVL树
	bool _IsAVLTree(Node* pRoot)
	{
		if (pRoot == nullptr)
			return true;
		int leftH = _Height(pRoot->_pLeft);
		int rightH = _Height(pRoot->_pRight);
		if (leftH - rightH > 1 || leftH - rightH < -1)
			return false;

		_IsAVLTree(pRoot->_pLeft);
		_IsAVLTree(pRoot->_pRight);
		return true;
	}
	size_t _Height(Node* pRoot)
	{
		if (pRoot == nullptr)
			return 0;
		int leftH = _Height(pRoot->_pLeft) + 1;
		int rightH = _Height(pRoot->_pRight) + 1;
		if (leftH > rightH)
		{
			return leftH;
		}
		else
		{
			return rightH;
		}
	}
	// 右单旋
	void RotateR(Node* pParent)
	{
		Node* grandparent = pParent->_pParent;
		Node* cur = pParent->_pLeft;
		Node* curRight = cur->_pRight;

		cur->_pRight = pParent;
		cur->_pParent = grandparent;

		pParent->_pLeft = curRight;
		pParent->_pParent = cur;

		if (curRight)
			curRight->_pParent = pParent;
		if (pParent == _pRoot)
		{
			_pRoot = cur;
		}
		else
		{
			if (grandparent->_pLeft == pParent)
			{
				grandparent->_pLeft = cur;
			}
			else
			{
				grandparent->_pRight = cur;
			}
		}
		pParent->_bf = cur->_bf = 0;
	}
	// 左单旋
	void RotateL(Node* pParent)
	{
		Node* grandparent = pParent->_pParent;
		Node* cur = pParent->_pRight;
		Node* curLeft = cur->_pLeft;

		cur->_pLeft = pParent;
		cur->_pParent = grandparent;

		pParent->_pRight = curLeft;
		pParent->_pParent = cur;

		if (curLeft)
			curLeft->_pParent = pParent;
		if (pParent == _pRoot)
		{
			_pRoot = cur;
		}
		else
		{
			if (grandparent->_pLeft == pParent)
			{
				grandparent->_pLeft = cur;
			}
			else
			{
				grandparent->_pRight = cur;
			}
		}
		pParent->_bf = cur->_bf = 0;
	}
	// 右左双旋
	void RotateRL(Node* pParent)
	{
		Node* cur = pParent->_pRight;
		Node* curLeft = cur->_pLeft;

		if (curLeft->_bf == 1)
		{
			RotateR(cur);
			RotateL(pParent);
			curLeft->_bf = 0;
			pParent->_bf = -1;
			cur->_bf = 0;
		}
		else if (curLeft->_bf == -1)
		{
			RotateR(cur);
			RotateL(pParent);
			cur->_bf = 1;
			pParent->_bf = curLeft->_bf = 0;
		}
		else
		{
			RotateR(cur);
			RotateL(pParent);
			pParent->_bf = curLeft->_bf = cur->_bf = 0;
		}

	}
	// 左右双旋
	void RotateLR(Node* pParent)
	{
		Node* cur = pParent->_pLeft;
		Node* curRight = cur->_pRight;

		if (curRight->_bf == 1)
		{
			RotateL(cur);
			RotateR(pParent);
			cur->_bf = -1;
			curRight->_bf = pParent->_bf = 0;
		}
		else if (curRight->_bf == -1)
		{
			RotateL(cur);
			RotateR(pParent);
			cur->_bf = curRight->_bf = 0;
			pParent->_bf = 1;
		}
		else
		{
			RotateL(cur);
			RotateR(pParent);
			pParent->_bf = curRight->_bf = cur->_bf = 0;
		}
	}


private:
	Node* _pRoot;
};
  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值