【C++】AVL树

一、概念

AVL树是一种平衡的二叉搜索树,它的特点是任何节点的两个子树的高度最大差别为1,也就是说它是高度平衡的。AVL树的查找、插入和删除的时间复杂度都是O(log n),但是插入和删除可能需要通过旋转来调整树的结构。AVL树是两位俄罗斯的数学家G.M.Adelson-Velskii和E.M.Landis在1962年发明的,达到降低树的高度,从而减少平均搜索长度的目的。

二、节点

cpp 复制代码
template<class T>
struct AVLTreeNode
{
	AVLTreeNode(const T& data = T())
		: _left(nullptr)
		, _right(nullptr)
		, _parent(nullptr)
		, _data(data)
		, _bf(0)
	{}

	AVLTreeNode<T>* _left;
	AVLTreeNode<T>* _right;
	AVLTreeNode<T>* _parent;
	T _data;
	int _bf;   // 节点的平衡因子
};

三、AVL树的插入

cpp 复制代码
		if (_root == nullptr)
		{
			_root = new Node(data);
			return true;
		}
		
		Node* cur = _root;
		Node* parent = _root;
		//找到data节点插入的位置
		while (cur)
		{
			if (data < cur->_data)
			{
				parent = cur;
				cur = cur->_left;
			}
			else if (data > cur->_data)
			{
				parent = cur;
				cur = cur->_right;
			}
		}
		//插入data节点
		Node* cur = new Node(data);
		if (data < parent->_data)
		{
			cur->_parent = parent;
			parent->_left = cur;
		}
		else
		{
			cur->_parent = parent;
			parent->_right = cur;
		}


	

更新平衡因子bf:

新增节点在左子树,父节点的平衡因子bf--;

新增节点在右子树,父节点的平衡因子bf++;

更新后:

1:父节点bf==0,父节点的子树高度不变,不必继续往上更新,插入结束。

2:父节点bf==1/-1,父节点的子树高度变化,必须往上更新

3:父节点bf==2/-2,父节点的子树违反规则,需要处理

cpp 复制代码
		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
			{
				//旋转

			}
		}

1、子树高度不变

子树高度不变,则不会继续往上影响祖先

注:当新插入节点的父节点的平衡因子bf变为0,则说明父节点的子树高度不变,即不用再往上更新,插入结束。

2、子树高度变化

子树高度变化,则会继续往上影响祖先

当平衡因子的绝对值大于1时,我们就需要对该子树进行调整。

AVL树的旋转

(1)右单旋

cpp 复制代码
	void RotateR(Node* parent)
	{
		Node* pprent = parent->_parent;
		Node* pl = parent->_left;
		Node* PRL = pl->_right;
		Node* PR = parent;

		//更新与上一节点的链接关系
		if (parent == _root)
		{
			_root = pl;
			pl->_parent = nullptr;
		}
		else
		{
			if (pprent->_left == parent)
			{
				pprent->_left = pl;
			}
			else
			{
				pprent->_right = pl;
			}

			pl->_parent = pprent;
		}

		
		pl->_right = PR;
		PR->_parent = pl;
		PR->_left = PRL;
		if(PRL)
			PRL->_parent = PR;


		pl->_bf = PR->_bf = 0;
	}

(2)左单旋

cpp 复制代码
	void RotateL(Node* parent)
	{
		Node* pprent = parent->_parent;
		Node* pr = parent->_right;
		Node* PLR = pr->_left;
		Node* PL = parent;

		//更新与上一节点的链接关系
		if (parent == _root)
		{
			_root = pr;
			pr->_parent = nullptr;
		}
		else
		{
			if (pprent->_left == parent)
			{
				pprent->_left = pr;
			}
			else
			{
				pprent->_right = pr;
			}

			pr->_parent = pprent;

		}

		
		pr->_left = PL;
		PL->_parent = pr;
		PL->_right = PLR;
		if(PLR)
			PLR->_parent = PL;

		//更新平衡因子
		pr->_bf = PL->_bf = 0;
	}

(3)左-右旋转

cpp 复制代码
	// 左右双旋
	void RotateLR(Node* parent)
	{
		Node* PR = parent;
		Node* PL = parent->_left;
		Node* P = PL->_right;
		int bf = P->_bf;

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

		if (bf == 0)
		{
			PL->_bf = 0;
			PR->_bf = 0;
			P->_bf = 0;
		}
		else if(bf == 1)
		{
			P->_bf = PR->_bf = 0;
			PL->_bf = -1;
		}
		else if (bf == -1)
		{
			P->_bf = PL->_bf = 0;
			PR->_bf = 1;
		}

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

(4)右-左旋转

cpp 复制代码
	// 右左双旋
	void RotateRL(Node* parent)
	{
		Node* PL = parent;
		Node* PR = parent->_right;
		Node* P = PR->_left;
		int bf = P->_bf;

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

		if(bf == 0)
		{
			PL->_bf = 0;
			PR->_bf = 0;
			P->_bf = 0;

		}
		else if(bf == -1)
		{
			P->_bf = PL->_bf = 0;
			PR->_bf = 1;
		}
		else if (bf == 1)
		{
			P->_bf = PR->_bf = 0;
			PL->_bf = -1;
		}

	}

完整代码:

cpp 复制代码
	bool Insert(const T& data)
	{
		if (_root == nullptr)
		{
			_root = new Node(data);
			return true;
		}
		
		Node* cur = _root;
		Node* parent = _root;
		//找到data节点插入的位置
		while (cur)
		{
			if (data < cur->_data)
			{
				parent = cur;
				cur = cur->_left;
			}
			else if (data > cur->_data)
			{
				parent = cur;
				cur = cur->_right;
			}
		}
		//插入data节点
		 cur = new Node(data);
		if (data < parent->_data)
		{
			cur->_parent = parent;
			parent->_left = cur;
		}
		else
		{
			cur->_parent = parent;
			parent->_right = cur;
		}

		//更新平衡因子
		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
			{
				//旋转
				//1.左单旋
				if (parent->_bf == 2 && cur->_bf == 1)
				{
					RotateL(parent);
				}

				//2.右单旋
				if (parent->_bf == -2 && cur->_bf == -1)
				{
					RotateR(parent);
				}

				//3.左-右旋转
				if (parent->_bf == -2 && cur->_bf == 1)
				{
					RotateLR(parent);
				}


				//4.右-左旋转
				if (parent->_bf == 2 && cur->_bf == -1)
				{
					RotateRL(parent);
				}

				//旋转过后无需再向上更新
				break;
			}

		}
		return true;

	}

四、AVL树的验证

cpp 复制代码
	bool IsAVLTree()
	{
		return _IsAVLTree(_root);
	}

	
	// 根据AVL树的概念验证pRoot是否为有效的AVL树
	bool _IsAVLTree(Node* root)
	{
		if (root == nullptr)
		{
			return true;
		}

		int lefth = _Height(root->_left);
		int righth = _Height(root->_right);
		if (righth - lefth != root->_bf)
		{
			cout << root->_data << ":bf false" << endl;
			return false;
		}

		return abs(lefth - righth) < 2 && _IsAVLTree(root->_left) && _IsAVLTree(root->_right);
	}
	size_t _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;
	}
相关推荐
@haihi12 分钟前
冒泡排序,插入排序,快速排序,选择排序
数据结构·算法·排序算法
丢掉幻想准备斗争31 分钟前
数据结构(栈和队列的实现)
数据结构
zengy51 小时前
Effective C++中文版学习记录(三)
数据结构·c++·学习·stl
&梧桐树夏5 小时前
【算法系列-链表】删除链表的倒数第N个结点
数据结构·算法·链表
QuantumStack5 小时前
【C++ 真题】B2037 奇偶数判断
数据结构·c++·算法
wclass-zhengge6 小时前
数据结构篇(绪论)
java·数据结构·算法
Dylanioucn6 小时前
【分布式微服务云原生】探索Redis:数据结构的艺术与科学
数据结构·redis·分布式·缓存·中间件
何事驚慌6 小时前
2024/10/5 数据结构打卡
java·数据结构·算法
结衣结衣.6 小时前
C++ 类和对象的初步介绍
java·开发语言·数据结构·c++·笔记·学习·算法
大三觉醒push亡羊补牢女娲补天版6 小时前
数据结构之排序(5)
数据结构