【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;
	}
相关推荐
INGNIGHT35 分钟前
91 · 最小调整代价(序列dp)
数据结构·算法·leetcode
从小白到软工5 小时前
5-2 从查表到控件:造按钮
c语言·开发语言·数据结构·经验分享·ai编程
David猪大卫7 小时前
【C++修炼】STL容器—stack与queue
c语言·开发语言·数据结构·c++·经验分享·笔记·学习
瑶池酒剑仙7 小时前
C++变量初始化五法完全指南:默认/拷贝/直接/列表/值初始化与窄化转换陷阱(青云之志)
c语言·开发语言·数据结构·c++·算法·visual studio
秋天的怀念_David9 小时前
听故事学语言 · 数据结构篇 · 序章一 算法之问
数据结构·程序人生·算法
-dzk-9 小时前
【链表】LC 25.K 个一组翻转链表
数据结构·链表
careathers10 小时前
【数据结构】基于比较的排序算法 - 插入排序
java·数据结构·排序算法
MicrosoftCloud10 小时前
数据结构 07(上)|查找:顺序、折半、分块,ASL 到底怎么
数据结构·查找·顺序查找·折半查找·分块查找·平均查找长度·asl
Mortalbreeze10 小时前
0 - 1 BFS 算法详解:从基本原理到经典题目建模
数据结构·c++·算法·图论·宽度优先
shehuiyuelaiyuehao1 天前
算法56,重排链表
数据结构·算法·链表