[C++]19:实现AVL树

本文介绍了AVL树的必要性,包括其在避免搜索性能下降的问题,以及如何通过平衡因子控制高度差。详细讲解了AVL树的插入过程,涉及旋转操作(左旋、右旋、左右旋和右左旋),并展示了计算子树高度和检查树是否正确的算法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一.为什么需要平衡二叉搜索树(AVL树)?

1.简单引入:

1,在正常的二叉搜索树中存在插入数据按照二叉搜索树的规则为单枝的情况。
2.假设插入了N个数据在二叉搜索树中这N个数据都是在一个单枝上面。
3.进行find操作找道一个数据从正常搜索树的高度次变成了N次。

2.AVL树的性质:

1.AVL树的子树都必须是AVL树:
2.左右子树的高度差不超过1,当前树就是一个二叉搜索树。
3.AVL树满足二叉搜索树的所有性质:

3.通过什么方式去控制高度差?

平衡因子:
1.平衡因子记录当前节点的左右子树的高度差值。
2.平衡因子的值默认为:右子树高度减左子树高度。

4. 基本结构:

template<class T>
struct AVLTreeNode {
	typedef AVLTreeNode<T>* Node;
	typedef T date;

	//1.构造函数:
	AVLTreeNode()
		:_left(nullptr)
		,_right(nullptr)
		,_parent(nullptr)
		,_date(date())
		,_bf(0)

	Node _left;//左子树
	Node _right;//右子树
	Node _parent;//节点的父节点
	date _date;//数据
	int _bf;//平衡因子
};

为什么考虑加入一个当前节点的父节点呢?
1.当前节点的左右高度差变化有可能会影响当前节点父节点的平衡因子的变化:
2.考虑在节点中加一个父节点的指针。

二.实现AVL树:

0.中序遍历

1.验证树是否为二叉搜索树。
2.同时打印平衡因子:

//中序遍历:
	void _Inorder(Node cur)
	{
		if (cur == nullptr)
			return;

		_Inorder(cur->_left);
		cout << "date:" << cur->_date << "平衡因子:" << cur->_bf << endl;
		_Inorder(cur->_right);
	}

	void Inorder()
	{
		if (_root == nullptr)
			return;
		_Inorder(_root);
	}

1.实现简单情况插入:

1.不影响爷爷节点:

请添加图片描述

2.影响爷爷节点为1/-1:

请添加图片描述

	//1.当前二叉树中没有元素:
		if (_root == nullptr)
		{
			Node newnode = new AVLTreeNode<T>(date);
			_root = newnode;
		}
		//2.当前二叉树中有元素:
		else
		{
			//1.遍历找插入位置:
			Node cur = _root;
			Node prev = nullptr;

			while (cur != nullptr)
			{
				prev = cur;
				if (cur->_date > date)
				{
					cur = cur->_left;
				}
				else if (cur->_date < date)
				{
					cur = cur->_right;
				}
				else
				{
					return false;
				}
			}

			cur = new AVLTreeNode<T>(date);

			if (cur->_date > prev->_date)
			{
				prev->_right = cur;
			}
			else if (cur->_date < prev->_date)
			{
				prev->_left = cur;
			}
			cur->_parent = prev;

			//2.进行节点插入和平衡因子修改:

			//2-1:正常插入:右减左 
			//右插入--->_bf++
			//左插入--->_bf--

			//节点插入判断左右:当前节点左右都为空或者有一个为空进行判断!

			while (prev)
			{
				//左插
				if (prev->_left == cur)
				{
					(prev->_bf)--;
				}
				//右插
				else if (prev->_right == cur)
				{
					(prev->_bf)++;
				}

				//1.平衡了
				if (prev->_bf == 0)
				{
					break;
				}

				//2.爷爷节点被影响,向上进行调整。
				else if (prev->_bf == 1 || prev->_bf == -1)
				{
					cur = cur->_parent;
					prev = prev->_parent;
				}

				//3.旋转判断
				else if (prev!=nullptr && (prev->_bf == -2 || prev->_bf ==2))
				{
					
				}
			}
		}

		return true;
	}

2.实现旋转操作解决插入节点产生-2/2的情况:

1.旋转的意义:

1.保持搜索树的规则:
2.当前树丛不平衡道平衡:
3.降低当前树的高度:

2.左旋:

在这里插入图片描述

当h为1的时候:
在这里插入图片描述

//左旋:
	void left_turn(Node& parent)
	{
		Node subR = parent->_right;
		Node subRL = subR->_left;

		parent->_right = subRL;
		//特殊情况的判断:
		if (subRL != nullptr)
			subRL->_parent = parent;

		Node ppNode = parent->_parent;
		subR->_left = parent;
		parent->_parent = subR;

		//当前的parent是子树还是根

		//1.作为根:
		if (ppNode == nullptr)
			_root = subR;
		//2.作为子树考虑左右
		else
		{
			if (ppNode->_left == parent)
				ppNode->_left = subR;
			else if (ppNode->_right == parent)
				ppNode->_right = subR;
		}
		subR->_parent = ppNode;

		//考虑平衡因子变化:
		parent->_bf = 0;
		subR->_bf = 0;
	}

3.右旋:

在这里插入图片描述

当h为1的时候:

在这里插入图片描述

//右旋:
	void right_turn(Node& parent)
	{
		Node subL = parent->_left;
		Node subLR = subL->_right;

		parent->_left = subLR;
		//特殊情况的判断:
		if (subLR != nullptr)
			subLR->_parent = parent;

		Node ppNode = parent->_parent;
		subL->_right = parent;
		parent->_parent = subL;

		//当前的parent是子树还是根

		//1.作为根:
		if (ppNode == nullptr)
			_root = subL;
		//2.作为子树考虑左右
		else
		{
			if (ppNode->_left == parent)
				ppNode->_left = subL;
			else if (ppNode->_right == parent)
				ppNode->_right = subL;
		}
		subL->_parent = ppNode;

		//考虑平衡因子变化:
		parent->_bf = 0;
		subL->_bf = 0;
	}

4.左右旋转:

在这里插入图片描述

//左+右:
	void left_right_turn(Node& parent)
	{
		void left_right_turn(Node& parent)
	{
		Node subL = parent->_left;
		Node subLR = subL->_right;
		//记录节点的平衡因子确定是左边还是右边
		int bf = subLR->_bf;

		//1.右左双旋:
		right_turn(parent->_left);
		left_turn(parent);

		//2.平衡因子的变换--->不进行后续操作会变成全零:

		//右有
		if (bf == 1)
		{
			subL->_bf = 0;
			parent->_bf = 1;
		}
		//左有
		else if (bf == -1)
		{
			subL->_bf = -1;
			parent->_bf = 0;
		}
		//没有
		else if (bf == 0)
		{
			subL->_bf = 0;
			parent->_bf = 0;
		}
	}

5.右左旋转:

在这里插入图片描述

void right_left_turn(Node& parent)
	{
		Node subR = parent->_right;
		Node subRL = subR->_left;

		//记录节点的平衡因子确定是左边还是右边
		int bf = subRL->_bf;

		//1.右左双旋:
		right_turn(parent->_right);
		left_turn(parent);

		//2.平衡因子的变换--->不进行后续操作会变成全零:

		//右有
		if (bf == 1)
		{
			//subR->_bf = 0;
			//parent->_bf = -1;
		}
		//左有
		else if (bf == -1)
		{
			//subR->_bf = 1;
			//parent->_bf = 0;
		}
		//没有
		else if (bf == 0)
		{
			//subR->_bf = 0;
			//parent->_bf = 0;
		}
	}

3.计算子树高度:

int _hight(Node root)
	{
		if (root == nullptr)
			return 0;
		int hight_left = _hight(root->_left);
		int hight_right = _hight(root->_right);
		return (hight_left > hight_right ? hight_left + 1 : hight_right + 1);
	}

	int hight()
	{
		return _hight(_root);
	}

4.判断当前avl树是否正确:

bool _isbalanceavl(Node root , int& hight)
	{
		//使用后续遍历+返回形参数:
		if (root == nullptr)
		{
			hight = 0;
			return true;
		}

		int hight_left, hight_right = 0;
		if ((!_isbalanceavl(root->_left,hight_left))
			|| (!_isbalanceavl(root->_right,hight_right)))
		{
			return false;
		}

		if (abs(hight_right - hight_left) >= 2)
		{
			cout << "数据:" << root->_date << " hight:" << hight_right - hight_left << " 平衡因子:" << root->_bf << "不平衡" << endl;
			return false;
		}

		else if (hight_right-hight_left != root->_bf)
		{
			cout << "数据:" << root->_date << " hight:" << hight_right - hight_left << " 平衡因子:" << root->_bf << "平衡因子异常" << endl;
			return false;
		}

		hight = (hight_left > hight_right ? hight_left + 1 : hight_right + 1);
		return true;
	}

	bool isbalanceavl()
	{
		int hight = 0;
		return _isbalanceavl(_root,hight);
	}

请添加图片描述

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值