二叉搜索树进阶--AVL树详细实现过程

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

AVL树概念

一棵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 ) O(log_2 n) O(log2n)

AVL树实现

AVL树基础结构

  • AVL树节点定义
//AVL树节点定义
template <class T>
struct AVLNode
{
	T _val;
	//平衡因子
	int _bf;
	typedef AVLNode<T> Node;
	Node* _parent;
	Node* _left;
	Node* _right;

	AVLNode(const T& val = T())
		:_val(val)
		,_bf(0)
		,_parent(nullptr)
		,_left(nullptr)
		,_right(nullptr)
	{}
};
  • AVL树定义
template <class T>
class AVLTree
{
public:
	typedef AVLNode<T> Node;

private:
	Node* _root = nullptr;
};

插入

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

  1. 按照二叉搜索树的方式插入新节点
  2. 调整节点的平衡因子
	bool insert(const T& val)
	{
		if (_root == nullptr)
		{
			_root = new Node(val);
			return true;
		}
		Node* cur = _root;
		Node* parent = nullptr;
		while (cur)
		{
			parent = cur;
			if (cur->_val == val)
				return false;
			else if (cur->_val > val)
				cur = cur->_left;
			else
				cur = cur->_right;
		}
		cur = new Node(val);
		if (parent->_val > val)
			parent->_left = cur;
		else
			parent->_right = cur;
		cur->_parent = parent;

		//调整,从parent开始
		while (parent)
		{
			//更新parent的平衡因子,如果在左子树插入新节点-1,如果右子树插入+1
			if (parent->_left == cur)
				--parent->_bf;
			else
				++parent->_bf;
			//直到父节点的平衡因子为0时停止更新(平衡因子为0说明插入新节点对祖先父节点的平衡因子不会有影响)
			if (parent->_bf == 0)
				//停止更新
				break;
			//如果平衡因子不为0,则继续向上更新
			else if (parent->_bf == 1 || parent == -1)
			{
				cur = parent;
				parent = parent->_parent;
			}
			else if (abs(parent->_bf) == 2)
			{
				if (parent->_bf == -2 && cur->_bf == -1)
				{
					//左边的左边高
					//右旋
					RotateR(parent);
				}
				else if (parent->_bf == 2 && cur->_bf == 1)
				{
					//右边的右边高
					//左旋
					RotateL(parent);
				}
				else if (parent->_bf == -2 && cur->_bf == 1)
				{
					//保存subLR的bf
					Node* subLR = cur->_left;
					int bf = subRL->_bf;

					//左边的右边高
					//先左旋再右旋
					RotateL(cur);
					RotateR(parent);

					//修正平衡因子
					if (bf == 1)
					{
						cur->_bf = -1;
						parent->_bf = 0;
					}
					else if (bf == -1)
					{
						cur->_bf = 0;
						parent->_bf = 1;
					}
				}
				else if (parent->_bf == 2 && cur->_bf == -1)
				{
					//保存subRL的bf
					Node* subRL = cur->_left;
					int bf = subRL->_bf;

					//右边的左边高
					//先右旋再左旋
					RotateR(cur);
					RotateL(parent);
				
					//修正平衡因子
					if (bf == 1)
					{
						cur->_bf = 0;
						parent->_bf = -1;
					}
					else if (bf == -1)
					{
						cur->_bf = 1;
						parent->_bf = 0;
					}
				}
				break;
			}
		}
		return true;
	}

插入:右旋实现

由于平衡因子只能是-1,0或1,因此当平衡因子为-2时,出现了左子树的左边过高的问题,因此需要进行右旋操作使得二叉搜索树重新平衡。
因此进行过右旋操作后,47(-2)节点和35(-1)节点的平衡因子都变成0。
在这里插入图片描述

	//右旋操作结构如下:
	//          parent(-2)
	//subL(-1)
	//     subLR(0)
	void RotateR(Node* parent)
	{
		Node* subL = parent->_left;
		Node* subLR = subL->_right;

		subL->_right = parent;
		parent->_left = subLR;
		if (subLR)
			subLR->_parent = parent;
		//更新cur和父亲的父亲之间的连接
		//判断是否为根节点
		if (parent == _root)
		{
			_root = subL;
			subL->_parent = nullptr;
		}
		else
		{
			Node* pparent = parent->_parent;
			if (pparent->_left == parent)
				pparent->_left = subL;
			else
				pparent->_right = subL;
			subL->_parent = pparent;
		}
		
		//更新父亲的父亲为cur
		parent->_parent = subL;
		//更新平衡因子
		subL->_bf = parent->_bf = 0;
	}

插入:左旋实现

由于平衡因子只能是-1,0或1,因此当平衡因子为2时,出现了右子树的右边过高的问题,因此需要进行左旋操作使得二叉搜索树重新平衡。
过程与上面右旋类似。

	//左旋操作结构如下:
	//parent(2)
	//          subR(1)
	//  subRL(0)
	void RotateL(Node* parent)
	{
		Node* subR = parent->_right;
		Node* subRL = subR->_left;

		subR->_left = parent;
		parent->_right = subRL;
		if (subRL)
			subRL->_parent = parent;
		//更新cur和父亲的父亲之间的连接
		//判断是否为根节点
		if (parent == _root)
		{
			_root = subR;
			subR->_parent = nullptr;
		}
		else
		{
			Node* pparent = parent->_parent;
			if (pparent->_left == parent)
				pparent->_left = subR;
			else
				pparent->_right = subR;
			subR->_parent = pparent;
		}

		//更新父亲的父亲为cur
		parent->_parent = subR;
		//更新平衡因子
		subR->_bf = parent->_bf = 0;
	}

插入:左右双旋实现

  • 插入节点后右边的左边高:单纯使用左旋或者右旋不能解决。
    因此需要左右双旋:
    1.以cur为轴,进行右旋
    2.以parent为轴,进行左旋
    在这里插入图片描述
  • 左边的右边高:单纯使用左旋或者右旋不能解决。
    因此需要左右双旋:
    1.以cur为轴,进行左旋
    2.以parent为轴,进行右旋

测试是否为平衡二叉树

  1. 检查平衡因子
  2. 查看平衡因子是否和左右子树的高度差一致
	//计算树的高度
	int Height(Node* root)
	{
		if (root == nullptr)
			return 0;
		int left = Height(root->_left);
		int right = Height(root->right);
		return left > right ? left + 1 : right + 1;
	}

	//测试是否为平衡二叉树
	bool _isBalance(Node* root)
	{
		if (root == nullptr)
			return true;

		//查看平衡因子是否和左右子树的高度差一致
		int left = Height(root->_left);
		int right = Height(root->_right);
		if (right - left != root->_bf)
		{
			cout << "Node;" << root->_val << "bef:" << root->_bf << "height gap:" << right - left << endl;
			return false;
		}

		return abs(root->_bf) < 2
			&& _isBalance(root->_left)
			&& _isBalance(root->_right);
	}

AVL树完整实现代码:

//AVL树节点定义
template <class T>
struct AVLNode
{
	T _val;
	//平衡因子
	int _bf;
	typedef AVLNode<T> Node;
	Node* _parent;
	Node* _left;
	Node* _right;

	AVLNode(const T& val = T())
		:_val(val)
		,_bf(0)
		,_parent(nullptr)
		,_left(nullptr)
		,_right(nullptr)
	{}
};

template <class T>
class AVLTree
{
public:
	typedef AVLNode<T> Node;

	bool insert(const T& val)
	{
		if (_root == nullptr)
		{
			_root = new Node(val);
			return true;
		}
		Node* cur = _root;
		Node* parent = nullptr;
		while (cur)
		{
			parent = cur;
			if (cur->_val == val)
				return false;
			else if (cur->_val > val)
				cur = cur->_left;
			else
				cur = cur->_right;
		}
		cur = new Node(val);
		if (parent->_val > val)
			parent->_left = cur;
		else
			parent->_right = cur;
		cur->_parent = parent;

		//调整,从parent开始
		while (parent)
		{
			//更新parent的平衡因子,如果在左子树插入新节点-1,如果右子树插入+1
			if (parent->_left == cur)
				--parent->_bf;
			else
				++parent->_bf;
			//直到父节点的平衡因子为0时停止更新(平衡因子为0说明插入新节点对祖先父节点的平衡因子不会有影响)
			if (parent->_bf == 0)
				//停止更新
				break;
			//如果平衡因子不为0,则继续向上更新
			else if (parent->_bf == 1 || parent == -1)
			{
				cur = parent;
				parent = parent->_parent;
			}
			else if (abs(parent->_bf) == 2)
			{
				if (parent->_bf == -2 && cur->_bf == -1)
				{
					//左边的左边高
					//右旋
					RotateR(parent);
				}
				else if (parent->_bf == 2 && cur->_bf == 1)
				{
					//右边的右边高
					//左旋
					RotateL(parent);
				}
				else if (parent->_bf == -2 && cur->_bf == 1)
				{
					//保存subLR的bf
					Node* subLR = cur->_left;
					int bf = subRL->_bf;

					//左边的右边高
					//先左旋再右旋
					RotateL(cur);
					RotateR(parent);

					//修正平衡因子
					if (bf == 1)
					{
						cur->_bf = -1;
						parent->_bf = 0;
					}
					else if (bf == -1)
					{
						cur->_bf = 0;
						parent->_bf = 1;
					}
				}
				else if (parent->_bf == 2 && cur->_bf == -1)
				{
					//保存subRL的bf
					Node* subRL = cur->_left;
					int bf = subRL->_bf;

					//右边的左边高
					//先右旋再左旋
					RotateR(cur);
					RotateL(parent);
				
					//修正平衡因子
					if (bf == 1)
					{
						cur->_bf = 0;
						parent->_bf = -1;
					}
					else if (bf == -1)
					{
						cur->_bf = 1;
						parent->_bf = 0;
					}
				}
				break;
			}
		}
		return true;
	}

	//右旋操作结构如下:
	//          parent(-2)
	//subL(-1)
	//     subLR(0)
	void RotateR(Node* parent)
	{
		Node* subL = parent->_left;
		Node* subLR = subL->_right;

		subL->_right = parent;
		parent->_left = subLR;
		if (subLR)
			subLR->_parent = parent;
		//更新cur和父亲的父亲之间的连接
		//判断是否为根节点
		if (parent == _root)
		{
			_root = subL;
			subL->_parent = nullptr;
		}
		else
		{
			Node* pparent = parent->_parent;
			if (pparent->_left == parent)
				pparent->_left = subL;
			else
				pparent->_right = subL;
			subL->_parent = pparent;
		}
		
		//更新父亲的父亲为cur
		parent->_parent = subL;
		//更新平衡因子
		subL->_bf = parent->_bf = 0;
	}

	//右旋操作结构如下:
	//          parent(-2)
	//subL(-1)
	//     subLR(0)
	void RotateR(Node* parent)
	{
		Node* subL = parent->_left;
		Node* subLR = subL->_right;

		subL->_right = parent;
		parent->_left = subLR;
		if (subLR)
			subLR->_parent = parent;
		//更新cur和父亲的父亲之间的连接
		//判断是否为根节点
		if (parent == _root)
		{
			_root = subL;
			subL->_parent = nullptr;
		}
		else
		{
			Node* pparent = parent->_parent;
			if (pparent->_left == parent)
				pparent->_left = subL;
			else
				pparent->_right = subL;
			subL->_parent = pparent;
		}
		
		//更新父亲的父亲为cur
		parent->_parent = subL;
		//更新平衡因子
		subL->_bf = parent->_bf = 0;
	}

	//左旋操作结构如下:
	//parent(2)
	//          subR(1)
	//  subRL(0)
	void RotateL(Node* parent)
	{
		Node* subR = parent->_right;
		Node* subRL = subL->_left;

		subR->_left = parent;
		parent->_right = subRL;
		if (subRL)
			subRL->_parent = parent;
		//更新cur和父亲的父亲之间的连接
		//判断是否为根节点
		if (parent == _root)
		{
			_root = subR;
			subR->_parent = nullptr;
		}
		else
		{
			Node* pparent = parent->_parent;
			if (pparent->_left == parent)
				pparent->_left = subR;
			else
				pparent->_right = subR;
			subR->_parent = pparent;
		}

		//更新父亲的父亲为cur
		parent->_parent = subR;
		//更新平衡因子
		subR->_bf = parent->_bf = 0;
	}

	void inorder()
	{
		_inorder(_root);
		cout << endl;
	}

	void _inorder(Node* root)
	{
		if (root)
		{
			_inorder(root->_left);
			cout << root->_val << " ";
			_inorder(root->_right);
		}
	}

	//计算树的高度
	int Height(Node* root)
	{
		if (root == nullptr)
			return 0;
		int left = Height(root->_left);
		int right = Height(root->right);
		return left > right ? left + 1 : right + 1;
	}

	//测试是否为平衡二叉树
	bool _isBalance(Node* root)
	{
		if (root == nullptr)
			return true;

		//查看平衡因子是否和左右子树的高度差一致
		int left = Height(root->_left);
		int right = Height(root->_right);
		if (right - left != root->_bf)
		{
			cout << "Node;" << root->_val << "bef:" << root->_bf << "height gap:" << right - left << endl;
			return false;
		}

		return abs(root->_bf) < 2
			&& _isBalance(root->_left)
			&& _isBalance(root->_right);
	}

private:
	Node* _root = nullptr;
};

void test()
{
	srand(time(nullptr));
	cout << "num: " << endl;
	int num;
	cin >> num;
	AVLTree<int> avl;
	for (int i = 0; i < num; ++i)
	{
		avl.insert(rand());
	}
	avl.inorder();
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值