数据结构 - AVL树


一、AVL树的介绍

1、概念

AVL树(Adelson-Velsky and Landis Tree)是一种自平衡的二叉搜索树。它得名于其发明者G. M. Adelson-Velsky和E. M. Landis。在AVL树中,任何节点的两个子树的高度最大差别为1,这保证了树的平衡性,从而避免了在极端情况下(如数据有序或接近有序时)二叉搜索树退化为链表,导致操作效率低下的问题。

在这里插入图片描述

2、特点

(1)平衡性:AVL树通过维护每个节点的平衡因子(左子树高度与右子树高度之差)来保持树的平衡。平衡因子的值只能是-1、0或1。如果某个节点的平衡因子绝对值大于1,那么该树就失去了平衡,需要通过旋转操作来重新平衡。

(2)旋转操作:当AVL树失去平衡时,会触发旋转操作来恢复平衡。旋转操作主要有四种:右旋(单旋)、左旋(单旋)、右-左双旋和左-右双旋。这些旋转操作通过改变树中节点的链接关系来降低树的高度,从而保持树的平衡。

(3) 高效的查找、插入和删除操作:由于AVL树保持了平衡,其查找、插入和删除操作的时间复杂度都能保持在O(log
n)的范围内,其中n是树中节点的数量。这使得AVL树在处理大量数据时能够保持高效的性能。

(4)空间开销:与普通的二叉搜索树相比,AVL树需要额外的空间来存储每个节点的平衡因子。这增加了树的空间开销,但在大多数情况下,这种开销是可以接受的。

3、应用场景

AVL树广泛应用于需要频繁插入、删除和查找操作的场景,如数据库索引、文件系统的目录结构、实时数据更新等。在这些场景中,AVL树能够保持高效的性能,确保数据处理的快速响应。

4、缺点

尽管AVL树具有许多优点,但它也有一些缺点。例如,在每次插入或删除节点后都需要进行平衡检查和可能的旋转操作,这增加了操作的复杂度。此外,与红黑树等其他自平衡二叉搜索树相比,AVL树在插入和删除操作时可能需要更多的旋转操作来保持平衡。因此,在某些特定场景下,红黑树可能比AVL树更受欢迎。然而,在需要高度平衡的场合,AVL树仍然是一个非常好的选择。

二、AVL树的实现

1、基本框架

树节点:


//树节点
template<class K, class V>
struct AVLTreeNode
{
	//构造函数
	AVLTreeNode(const pair<K, V>& val = pair<K, V>())
		: _left(nullptr)
		, _right(nullptr)
		, _parent(nullptr)
		, _val(val)
		, _bf(0)
	{}

	AVLTreeNode<K,V>* _left;	//左孩子指针
	AVLTreeNode<K, V>* _right;	//右孩子指针
	AVLTreeNode<K, V>* _parent;	//父亲指针
	pair<K, V> _val;	//数据
	int _bf;   //节点的平衡因子 -> 右子树高度 - 左子树高度
};

AVL树类:

template<class K, class V>
class AVLTree
{
	//树节点
	typedef AVLTreeNode<K, V> Node;
	private:
	//根节点
	Node* _root = nullptr;
};

2、查找

AVL树的查找操作与普通的二叉搜索树(BST)的查找操作非常相似。由于AVL树本身就是一种二叉搜索树,所以它可以利用二叉搜索树的性质来高效地查找元素。在AVL树中查找元素的过程基本上不涉及树的平衡操作(如旋转),只是简单地利用节点的值和树的结构来定位目标元素。

步骤:从根节点(cur)开始,通过判断key与当前节点key的大小来决定是去左子树还是右子树找该值,循环迭代(cur)上述过程直到找到相同的key后返回该节点,否则就返回空。

//查找
Node* Find(const K& key)
{
	//从根节点开始
	Node* cur = _root;
	while (cur)
	{
		//大了就去左子树中搜索
		if (cur->_val.first > key)
		{
			cur = cur->_left;
		}
		//小了就去右子树中搜索
		else if (cur->_val.first < key)
		{
			cur = cur->_right;
		}
		else
		{
			//找到返回当前节点
			return cur;
		}
	}
	return nullptr;
}

在这里插入图片描述

3、插入

AVL树的插入操作在二叉搜索树(BST)的插入基础上增加了维护树平衡的步骤。AVL树是一种自平衡的二叉搜索树,其中任何节点的两个子树的高度最大差别为1。

在插入后需要更新平衡因子,插入左子树节点时,父节点的平衡因子-1,插入右子树节点时,父节点的平衡因子+1,当更新后的父节点的平衡因子为0就不需要向上更新了(说明插入之前是-1或者1,插入节点后不会影响到上面的节点的平衡因子),当平衡因子(右子树高度 - 左子树高度)为1或者-1时需要向上更新平衡因子(说明插入之前为0,插入后会影响到上面节点的平衡因子),当为2或者-2时说明该树失去平衡了,需要旋转来调整高度,旋转之后的高度就平衡了不需要向上更新。

在这里插入图片描述

(1)插入一个元素使左子树高度大于右子树且失去平衡(单纯一边高(L和parent平衡因子同号)) ------- 右旋转
在这里插入图片描述
平衡因子:根据图分析,旋转后parent和L都为0

// 右单旋
void RotateR(Node* parent)
{
	//左节点
	Node* L = parent->_left;
	//左子树右边第一个节点
	Node* Lr = L->_right;
	//parent的父亲
	Node* pparent = parent->_parent;

	//连接过程
	L->_right = parent;
	parent->_parent = L;

	//该节点可能为空
	if (Lr)
	{
		Lr->_parent = parent;
	}
	parent->_left = Lr;

	//更新L的父节点
	L->_parent = pparent;

	//是根的情况
	if (pparent == nullptr)
	{
		_root = L;
	}
	else
	{
		if (parent == pparent->_left) pparent->_left = L;
		else pparent->_right = L;
	}

	//更新后平衡因子都为0
	parent->_bf = L->_bf = 0;
}

(2)插入一个元素使右子树高度大于左子树且失去平衡(单纯一边高(R和parent平衡因子同号)) ------- 左旋转
在这里插入图片描述

平衡因子:根据图分析,旋转后parent和R都为0

//左旋转
void RotateL(Node* parent)
{
	//右边第一个节点
	Node* R = parent->_right;
	//右子树第一个左节点
	Node* Rl = R->_left;
	//父节点
	Node* pparent = parent->_parent;

	//连接过程

	parent->_right = Rl;
	if (Rl)
	{
		Rl->_parent = parent;
	}

	R->_left = parent;
	//更新parent的父节点
	parent->_parent = R;
	//更新R的父节点
	R->_parent = pparent;
	//是根的情况
	if (nullptr == pparent)
	{
		_root = R;
	}
	else
	{
		if (pparent->_left == parent) pparent->_left = R;
		else pparent->_right = R;
	}


	//更新平衡因子
	parent->_bf = R->_bf = 0;
}

(3)插入一个元素使左子树高度大于右子树且失去平衡(不单纯一边高(L和parent平衡因子异号)) ------- 左右旋转
在这里插入图片描述

平衡因子:根据图分析(上面只有Lr = 1 的情况,Lr = 0,或者Lr = -1的情况也按上图的方式推导),更新前Lr的平衡因子为1时更新后L的平衡因子为-1、parent和Lr为0,更新前Lr的平衡因子为-1时更新后parent平衡因子为1、L和Lr平衡因子为0,更新前Lr的平衡因子为0时,L、Lr、parent平衡因子都为0。

// 左右双旋
void RotateLR(Node* parent)
{
	Node* L = parent->_left;
	Node* Lr = L->_right;
	//先保存Lr的平衡因子,因为旋转之后Lr的平衡因子会变
	int bf = Lr->_bf;
	//左右双旋
	RotateL(L);
	RotateR(parent);

	//更新平衡因子
	if (bf == -1)
	{
		parent->_bf = 1;
		L->_bf = 0;
		Lr->_bf = 0;
	}
	else if (bf == 1)
	{
		parent->_bf = 0;
		L->_bf = -1;
		Lr->_bf = 0;
	}
	else if (bf == 0)
	{
		parent->_bf = 0;
		L->_bf = 0;
		Lr->_bf = 0;
	}
	else
		assert(false);
}

(4)插入一个元素使右子树高度大于左子树且失去平衡(不单纯一边高(R和parent平衡因子异号)) ------- 右左旋转
在这里插入图片描述
平衡因子:根据图分析(上面只有Rl = 1 的情况,Rl = 0,或者Rl = -1的情况也按上图的方式推导),更新前Rl的平衡因子为-1时更新后R的平衡因子为-1、parent和Rl为0,更新前Rl的平衡因子为1时更新后parent平衡因子为-1、R和Rl平衡因子为0,更新前Rl的平衡因子为0时,R、Rl、parent平衡因子都为0。

// 右左双旋
void RotateRL(Node* parent)
{
	Node* R = parent->_right;
	Node* Rl = R->_left;
	//先保存平衡因子
	int bf = Rl->_bf;

	//右左双旋
	RotateR(R);
	RotateL(parent);

	//更新平衡因子
	if (bf == 1)
	{
		parent->_bf = -1;
		R->_bf = 0;
		Rl->_bf = 0;
	}
	else if (bf == -1)
	{
		parent->_bf = 0;
		R->_bf = 1;
		Rl->_bf = 0;
	}
	else if (bf == 0)
	{
		parent->_bf = 0;
		R->_bf = 0;
		Rl->_bf = 0;
	}
	else
		assert(false);
}

(5)根据插入节点(参考搜索二叉树)+更新平衡因子完成插入

//插入
bool Insert(const pair<K, V>& val)
{	
	//找到放val的位置
	Node* cur = _root;
	//作为前驱指针,与val节点连接
	Node* precursor = nullptr;
	while (cur)
	{
		//向左
		if (cur->_val.first > val.first)
		{
			precursor = cur;
			cur = cur->_left;
		}
		//向右
		else if (cur->_val.first < val.first)
		{
			precursor = cur;
			cur = cur->_right;
		}
		//存在相同的值
		else
		{
			return false;
		}
	}
	
	//插入新节点
	cur = new Node(val);
	//不存在根节点,作为根节点
	if (precursor == nullptr) _root = cur;
	//连接在前驱指针左侧
	else if (precursor->_val.first > val.first)
	{
		cur->_parent = precursor;
		precursor->_left = cur;
	}
	//连接在前驱指针右侧
	else
	{
		cur->_parent = precursor;
		precursor->_right = cur;
	}
	
	//更新平衡因子
	while (precursor)
	{

		if (precursor->_left == cur)
			precursor->_bf--;
		else
			precursor->_bf++;

		//为0说明平衡了,不需要再更新了
		if (precursor->_bf == 0)
			break;
		//出现异常需要更新平衡因子,更新完就可以
		else if (precursor->_bf == 2 || precursor->_bf == -2)
		{
			if (precursor->_bf == 2 && cur->_bf == 1)
			{
				RotateL(precursor);
			}
			else if (precursor->_bf == -2 && cur->_bf == -1)
			{
				RotateR(precursor);
			}
			else if (precursor->_bf == 2 && cur->_bf == -1)
			{
				RotateRL(precursor);
			}
			else if (precursor->_bf == -2 && cur->_bf == 1)
			{
				RotateLR(precursor);
			}
			else
			{
				assert(false);
			}
			//更新完平衡了不需要向上更新
			break;
		}

		//进行迭代(向上更新)
		cur = precursor;
		precursor = precursor->_parent;
	}

	return true;
}

4、删除

AVL树删除操作是一个复杂但关键的过程,因为它需要在删除节点后重新调整树的结构以保持其平衡性。AVL树是一种自平衡的二叉搜索树,其中任何节点的两个子树的高度最大差别为1。

在删除后需要更新平衡因子,删除左子树节点时,父节点的平衡因子+1,删除右子树节点时,父节点的平衡因子-1,当更新后的父节点的平衡因子为-1 或者 1就不需要向上更新了(说明删除之前0,删除节点后不会影响到上面的节点的平衡因子),当平衡因子0时需要向上更新平衡因子(说明删除之前为1或者-1,删除后会影响到上面节点的平衡因子),当为2或者-2时说明该树失去平衡了,需要旋转来调整高度,旋转之后当前父节点还是0的话还要继续向上更新,直到更新为-1或者1时就结束更新。

因为复用插入的旋转操作,所以在删除元素后有一些平衡因子在旋转过程中没有正确更新,此时我们就要在旋转完后再次更新。
(1)删除后,右边高了,当R = 0,或者 R = 1 时进行 — 左单旋
在这里插入图片描述
平衡因子:当R的平衡因子为0时,parent的平衡因子需要修改为1,R的平衡因子修改为-1,当R的平衡因子为1时(也是按上图方式推导),parent、R平衡因子都为0。

(2)删除后,左边高了,当L = 0,或者 L = -1 时进行 — 右单旋
在这里插入图片描述
平衡因子:当L的平衡因子为0时,parent的平衡因子需要修改为-1,L的平衡因子修改为-1,当L的平衡因子为-1时(也是按上图方式推导),parent、L平衡因子都为0。

(3)删除后,右边高了,并且出现异号 — 右左双旋

在这里插入图片描述

平衡因子:与插入时使用的右左双旋一样。

(4)删除后,左边高了,并且出现异号 — 左右双旋

在这里插入图片描述
平衡因子:与插入时使用的左右双旋一样。

(5)使用删除操作(参考搜索二叉树的删除)+旋转完成删除

//删除
bool Erase(const K& key)
{
	//从根节点开始搜索
	Node* cur = _root;
	//作为cur的前驱指针
	Node* precursor = nullptr;
	
	//搜索查找
	while (cur)
	{
		if (cur->_val.first > key)
		{
			precursor = cur;
			cur = cur->_left;
		}
			
		else if (cur->_val.first < key)
		{
			precursor = cur;
			cur = cur->_right;
		}
			
		else
			break;
	}

	//找不到
	if (cur == nullptr) return false;

	//假设cur左右节点都存在,找右边最小值替换
	if (cur->_left != nullptr && cur->_right != nullptr)
	{
		Node* tmp1 = cur->_right;
		Node* tmp2 = nullptr;
	
		while (tmp1->_left)
		{
			tmp2 = tmp1;
			tmp1 = tmp1->_left;
		}

		cur->_val = tmp1->_val;
		//tmp1左边没有节点,自己就是最小的节点
		if (tmp2 == nullptr)
		{
			precursor = cur;
			cur = tmp1;	
		}
		else
		{
			cur = tmp1;
			precursor = tmp2;
		}
	}
		
	//假设左边为空和左右节点都为空
	int sign = 0;//左边为-1,右边为1
	Node* deletecur = cur;
	if (cur->_left == nullptr)
	{
		左边为空,父节点为空,cur为根节点,让cur->_riggt做为根,直接结束就行了(cur->_riggt本身为平衡树)
		if (precursor == nullptr)
		{
			_root = cur->_right;
			delete deletecur;
			return true;
		}
		else
		{
			if (precursor->_left == cur)
			{
				precursor->_left = cur->_right;
				if (cur->_right == nullptr) sign = -1;
			}
				
			else
			{
				precursor->_right = cur->_right;
				if (cur->_right == nullptr) sign = 1;
			}
				
		}
		cur = cur->_right;
	}
	//假设右边为空
	else
	{
		//右边为空,父节点为空,cur为根节点,让cur->_left做为根,直接结束就行了(cur->_left本身为平衡树)
		if (precursor == nullptr)
		{
			_root = cur->_left;
			delete deletecur;
			return true;
		}
		else
		{
			if (precursor->_left == cur)
				precursor->_left = cur->_left;
			else
				precursor->_right = cur->_left;
		}
		cur = cur->_left;
	}

	//更新平衡因子
	while (precursor)
	{
		//cur出现空的情况
		if (cur == nullptr)
		{
			if (sign == -1)
				precursor->_bf++;
			else
				precursor->_bf--;
		}
		else if (precursor->_left == cur)
			precursor->_bf++;
		else
			precursor->_bf--;

		if (precursor->_bf == -1 || precursor->_bf == 1)
			break;
		else if (precursor->_bf == -2 || precursor->_bf == 2)
		{
			//右边高了左单旋
			if (precursor->_bf == 2 && (precursor->_right->_bf == 0 || precursor->_right->_bf == 1))
			{
				//R会做为新的precursor先保存
				Node* R = precursor->_right;
				int bf = precursor->_right->_bf;
				RotateL(precursor);

				//在旋转后的平衡因子不符合预期,需要更新
				if (bf == 0)
				{
					precursor->_bf = 1;
					R->_bf = -1;
				}

				//更新
				precursor = R;
			}
			//左边高了右单旋
			else if (precursor->_bf == -2 && (precursor->_left->_bf == 0 || precursor->_left->_bf == -1))
			{
				//L会做为新的precursor先保存
				Node* L = precursor->_left;
				int bf = L->_bf;
				RotateR(precursor);

				//在旋转后的平衡因子不符合预期,需要更新
				if (bf == 0)
				{
					precursor->_bf = -1;
					L->_bf = 1;
				}

				//更新
				precursor = L;
			}

			else if (precursor->_bf == 2 && precursor->_right->_bf == -1)
			{
				//L会做为新的precursor先保存
				Node* R = precursor->_right;
				Node* L = R->_left;
				RotateRL(precursor);

				//更新
				precursor = L;
			}
			else if (precursor->_bf == -2 && precursor->_left->_bf== 1)
			{
				//R会做为新的precursor先保存
				Node* L = precursor->_left;
				Node* R = L->_right;
				RotateLR(precursor);

				//更新
				precursor = R;
			}
			else
				assert(false);

			//旋转完precursor更新了再次判断是否平衡了
			if (precursor->_bf == -1 || precursor->_bf == 1) break;
		}
		

		//进行迭代
		cur = precursor;
		precursor = precursor->_parent;

	}

	delete deletecur;
	return true;
}

5、测试

通过两颗子树的高度差是否大于1和判断高度差是否与这颗根节点平衡因子相等来判断是否为AVL树。

//判断是否为平衡树
bool IsBalanceTree()
{
	return _IsBalanceTree(_root);
}
//验证是否是平衡树
bool _IsBalanceTree(Node* root)
{
	if (root == nullptr) return true;

	int l = _Height(root->_left);
	int r = _Height(root->_right);
	int buff = r - l;
	if (root->_bf != buff || buff > 1 || buff < -1)
		return false;

	return _IsBalanceTree(root->_left) && _IsBalanceTree(root->_right);
}
//求树的高度
int _Height(Node* root)
{
	if (root == nullptr) return 0;

	int l = _Height(root->_left);
	int r = _Height(root->_right);

	return max(l, r) + 1;
}

void test()
{
	AVLTree<int, int> a;
	vector<int> arr;
	
	for (int i = 0; i < 10000; i++)
	{
		int m = rand() + i;
		arr.push_back(m);
	}
	//插入10000个随机数
	for (auto e :arr)
	{
		a.Insert({ e,e });
		if (a.IsBalanceTree())
		cout << "是AVL树" << endl;
		else
			assert(false);
	}
	
	//再全部删除
	for (auto e : arr)
	{
		a.Erase(e);
		if (a.IsBalanceTree())
			cout << "是AVL树" << endl;
		else
			assert(false);
	}
}

在这里插入图片描述
在插入和删除过程中没有报错,说明该树是AVL树。

6、总代码

#pragma once
#include<iostream>
#include<string>
#include<cassert>
#include<queue>
#include<vector>

using namespace std;

//树节点
template<class K, class V>
struct AVLTreeNode
{
	//构造函数
	AVLTreeNode(const pair<K, V>& val = pair<K, V>())
		: _left(nullptr)
		, _right(nullptr)
		, _parent(nullptr)
		, _val(val)
		, _bf(0)
	{}

	AVLTreeNode<K,V>* _left;	//左孩子指针
	AVLTreeNode<K, V>* _right;	//右孩子指针
	AVLTreeNode<K, V>* _parent;	//父亲指针
	pair<K, V> _val;	//数据
	int _bf;   //节点的平衡因子 -> 右子树高度 - 左子树高度
};

template<class K, class V>
class AVLTree
{
	//树节点
	typedef AVLTreeNode<K, V> Node;
public:

	~AVLTree()
	{
		DeleteTree(_root);
	}

	//插入
	bool Insert(const pair<K, V>& val)
	{	
		//找到放val的位置
		Node* cur = _root;
		//作为前驱指针,与val节点连接
		Node* precursor = nullptr;
		while (cur)
		{
			//向左
			if (cur->_val.first > val.first)
			{
				precursor = cur;
				cur = cur->_left;
			}
			//向右
			else if (cur->_val.first < val.first)
			{
				precursor = cur;
				cur = cur->_right;
			}
			//存在相同的值
			else
			{
				return false;
			}
		}
		
		//插入新节点
		cur = new Node(val);
		//不存在根节点,作为根节点
		if (precursor == nullptr) _root = cur;
		//连接在前驱指针左侧
		else if (precursor->_val.first > val.first)
		{
			cur->_parent = precursor;
			precursor->_left = cur;
		}
		//连接在前驱指针右侧
		else
		{
			cur->_parent = precursor;
			precursor->_right = cur;
		}
		
		//更新平衡因子
		while (precursor)
		{

			if (precursor->_left == cur)
				precursor->_bf--;
			else
				precursor->_bf++;

			//为0说明平衡了,不需要再更新了
			if (precursor->_bf == 0)
				break;
			//出现异常需要更新平衡因子,更新完就可以
			else if (precursor->_bf == 2 || precursor->_bf == -2)
			{
				if (precursor->_bf == 2 && cur->_bf == 1)
				{
					RotateL(precursor);
				}
				else if (precursor->_bf == -2 && cur->_bf == -1)
				{
					RotateR(precursor);
				}
				else if (precursor->_bf == 2 && cur->_bf == -1)
				{
					RotateRL(precursor);
				}
				else if (precursor->_bf == -2 && cur->_bf == 1)
				{
					RotateLR(precursor);
				}
				else
				{
					assert(false);
				}
				//更新完平衡了不需要向上更新
				break;
			}

			//进行迭代(向上更新)
			cur = precursor;
			precursor = precursor->_parent;
		}

		return true;
	}

	//查找
	Node* Find(const K& key)
	{
		//从根节点开始
		Node* cur = _root;
		while (cur)
		{
			//大了就去左子树中搜索
			if (cur->_val.first > key)
			{
				cur = cur->_left;
			}
			//小了就去右子树中搜索
			else if (cur->_val.first < key)
			{
				cur = cur->_right;
			}
			else
			{
				//找到返回当前节点
				return cur;
			}
		}
		return nullptr;
	}

	//删除
	bool Erase(const K& key)
	{
		//从根节点开始搜索
		Node* cur = _root;
		//作为cur的前驱指针
		Node* precursor = nullptr;
		
		//搜索查找
		while (cur)
		{
			if (cur->_val.first > key)
			{
				precursor = cur;
				cur = cur->_left;
			}
				
			else if (cur->_val.first < key)
			{
				precursor = cur;
				cur = cur->_right;
			}
				
			else
				break;
		}

		//找不到
		if (cur == nullptr) return false;

		//假设cur左右节点都存在,找右边最小值替换
		if (cur->_left != nullptr && cur->_right != nullptr)
		{
			Node* tmp1 = cur->_right;
			Node* tmp2 = nullptr;
		
			while (tmp1->_left)
			{
				tmp2 = tmp1;
				tmp1 = tmp1->_left;
			}

			cur->_val = tmp1->_val;
			//tmp1左边没有节点,自己就是最小的节点
			if (tmp2 == nullptr)
			{
				precursor = cur;
				cur = tmp1;	
			}
			else
			{
				cur = tmp1;
				precursor = tmp2;
			}
		}
			
		//假设左边为空和左右节点都为空
		int sign = 0;//左边为-1,右边为1
		Node* deletecur = cur;
		if (cur->_left == nullptr)
		{
			左边为空,父节点为空,cur为根节点,让cur->_riggt做为根,直接结束就行了(cur->_riggt本身为平衡树)
			if (precursor == nullptr)
			{
				_root = cur->_right;
				delete deletecur;
				return true;
			}
			else
			{
				if (precursor->_left == cur)
				{
					precursor->_left = cur->_right;
					if (cur->_right == nullptr) sign = -1;
				}
					
				else
				{
					precursor->_right = cur->_right;
					if (cur->_right == nullptr) sign = 1;
				}
					
			}
			cur = cur->_right;
		}
		//假设右边为空
		else
		{
			//右边为空,父节点为空,cur为根节点,让cur->_left做为根,直接结束就行了(cur->_left本身为平衡树)
			if (precursor == nullptr)
			{
				_root = cur->_left;
				delete deletecur;
				return true;
			}
			else
			{
				if (precursor->_left == cur)
					precursor->_left = cur->_left;
				else
					precursor->_right = cur->_left;
			}
			cur = cur->_left;
		}

		//更新平衡因子
		while (precursor)
		{
			//cur出现空的情况
			if (cur == nullptr)
			{
				if (sign == -1)
					precursor->_bf++;
				else
					precursor->_bf--;
			}
			else if (precursor->_left == cur)
				precursor->_bf++;
			else
				precursor->_bf--;

			if (precursor->_bf == -1 || precursor->_bf == 1)
				break;
			else if (precursor->_bf == -2 || precursor->_bf == 2)
			{
				//右边高了左单旋
				if (precursor->_bf == 2 && (precursor->_right->_bf == 0 || precursor->_right->_bf == 1))
				{
					//R会做为新的precursor先保存
					Node* R = precursor->_right;
					int bf = precursor->_right->_bf;
					RotateL(precursor);

					//在旋转后的平衡因子不符合预期,需要更新
					if (bf == 0)
					{
						precursor->_bf = 1;
						R->_bf = -1;
					}

					//更新
					precursor = R;
				}
				//左边高了右单旋
				else if (precursor->_bf == -2 && (precursor->_left->_bf == 0 || precursor->_left->_bf == -1))
				{
					//L会做为新的precursor先保存
					Node* L = precursor->_left;
					int bf = L->_bf;
					RotateR(precursor);

					//在旋转后的平衡因子不符合预期,需要更新
					if (bf == 0)
					{
						precursor->_bf = -1;
						L->_bf = 1;
					}

					//更新
					precursor = L;
				}

				else if (precursor->_bf == 2 && precursor->_right->_bf == -1)
				{
					//L会做为新的precursor先保存
					Node* R = precursor->_right;
					Node* L = R->_left;
					RotateRL(precursor);

					//更新
					precursor = L;
				}
				else if (precursor->_bf == -2 && precursor->_left->_bf== 1)
				{
					//R会做为新的precursor先保存
					Node* L = precursor->_left;
					Node* R = L->_right;
					RotateLR(precursor);

					//更新
					precursor = R;
				}
				else
					assert(false);

				//旋转完precursor更新了再次判断是否平衡了
				if (precursor->_bf == -1 || precursor->_bf == 1) break;
			}
			

			//进行迭代
			cur = precursor;
			precursor = precursor->_parent;

		}

		delete deletecur;
		return true;
	}

	//判断是否为平衡树
	bool IsBalanceTree()
	{
		return _IsBalanceTree(_root);
	}
	
private:

	// 右单旋
	void RotateR(Node* parent)
	{
		//左节点
		Node* L = parent->_left;
		//左子树右边第一个节点
		Node* Lr = L->_right;
		//parent的父亲
		Node* pparent = parent->_parent;

		//连接过程
		L->_right = parent;
		parent->_parent = L;

		//该节点可能为空
		if (Lr)
		{
			Lr->_parent = parent;
		}
		parent->_left = Lr;

		//更新L的父节点
		L->_parent = pparent;

		//是根的情况
		if (pparent == nullptr)
		{
			_root = L;
		}
		else
		{
			if (parent == pparent->_left) pparent->_left = L;
			else pparent->_right = L;
		}

		//更新后平衡因子都为0
		parent->_bf = L->_bf = 0;
	}

	//左旋转
	void RotateL(Node* parent)
	{
		//右边第一个节点
		Node* R = parent->_right;
		//右子树第一个左节点
		Node* Rl = R->_left;
		//父节点
		Node* pparent = parent->_parent;

		//连接过程

		parent->_right = Rl;
		if (Rl)
		{
			Rl->_parent = parent;
		}

		R->_left = parent;
		//更新parent的父节点
		parent->_parent = R;
		//更新R的父节点
		R->_parent = pparent;
		//是根的情况
		if (nullptr == pparent)
		{
			_root = R;
		}
		else
		{
			if (pparent->_left == parent) pparent->_left = R;
			else pparent->_right = R;
		}


		//更新平衡因子
		parent->_bf = R->_bf = 0;
	}

	// 右左双旋
	void RotateRL(Node* parent)
	{
		Node* R = parent->_right;
		Node* Rl = R->_left;
		//先保存平衡因子
		int bf = Rl->_bf;

		//右左双旋
		RotateR(R);
		RotateL(parent);

		//更新平衡因子
		if (bf == 1)
		{
			parent->_bf = -1;
			R->_bf = 0;
			Rl->_bf = 0;
		}
		else if (bf == -1)
		{
			parent->_bf = 0;
			R->_bf = 1;
			Rl->_bf = 0;
		}
		else if (bf == 0)
		{
			parent->_bf = 0;
			R->_bf = 0;
			Rl->_bf = 0;
		}
		else
			assert(false);
	}

	// 左右双旋
	void RotateLR(Node* parent)
	{
		Node* L = parent->_left;
		Node* Lr = L->_right;
		//先保存Lr的平衡因子,因为旋转之后Lr的平衡因子会变
		int bf = Lr->_bf;
		//左右双旋
		RotateL(L);
		RotateR(parent);

		//更新平衡因子
		if (bf == -1)
		{
			parent->_bf = 1;
			L->_bf = 0;
			Lr->_bf = 0;
		}
		else if (bf == 1)
		{
			parent->_bf = 0;
			L->_bf = -1;
			Lr->_bf = 0;
		}
		else if (bf == 0)
		{
			parent->_bf = 0;
			L->_bf = 0;
			Lr->_bf = 0;
		}
		else
			assert(false);
	}
	
	//求树的高度
	int _Height(Node* root)
	{
		if (root == nullptr) return 0;

		int l = _Height(root->_left);
		int r = _Height(root->_right);

		return max(l, r) + 1;
	}
	
	//验证是否是平衡树
	bool _IsBalanceTree(Node* root)
	{
		if (root == nullptr) return true;

		int l = _Height(root->_left);
		int r = _Height(root->_right);
		int buff = r - l;
		if (root->_bf != buff || buff > 1 || buff < -1)
			return false;

		return _IsBalanceTree(root->_left) && _IsBalanceTree(root->_right);
	}

	void DeleteTree(Node* root)
	{
		if (root == nullptr)
			return;

		DeleteTree(root->_left);
		DeleteTree(root->_right);

		delete root;
	}

	//根节点
	Node* _root = nullptr;
};

三、AVL树的性能

1、搜索性能分析
时间复杂度:O(log n)

(1) AVL树作为二叉搜索树的一种,其搜索操作与普通的二叉搜索树相同。从根节点开始,根据要搜索的值与节点值的大小关系,决定是向左子树搜索还是向右子树搜索,直到找到目标节点或搜索到叶子节点为止。
(2)由于AVL树的高度被限制在O(log n)以内(其中n是树中节点的数量),因此搜索操作的时间复杂度也是O(log n)。

2、插入性能分析
时间复杂度:O(log n)

(1)插入操作首先执行普通的二叉搜索树插入操作,找到新节点应该插入的位置。 插入新节点后,从该节点开始向上遍历,更新所有受影响节点的平衡因子。
(2)如果某个节点的平衡因子变为2或-2(即左右子树高度差超过1),则需要进行旋转操作来恢复平衡。旋转操作包括单旋转(左单旋、右单旋)和双旋转(左右双旋、右左双旋)。
(3)由于旋转操作只在从插入节点到根节点的路径上进行,且路径长度不超过树的高度,因此插入操作的时间复杂度也是O(log n)。

3、删除性能分析
时间复杂度:O(log n)

(1)删除操作首先找到要删除的节点。
(2)如果要删除的节点有两个子节点,则通常使用其右子树中的最小节点(或左子树中的最大节点)来替换它,并删除那个最小(或最大)节点。
(3)删除节点后,从该节点开始向上遍历,更新所有受影响节点的平衡因子。
(4)如果某个节点的平衡因子变为2或-2,则需要进行旋转操作来恢复平衡。旋转操作的类型与插入操作类似,包括单旋转和双旋转。
(5)同样地,由于旋转操作只在从删除节点到根节点的路径上进行,且路径长度不超过树的高度,因此删除操作的时间复杂度也是O(log n)。

4、总结

AVL树通过保持树的平衡性,使得搜索、插入和删除操作的时间复杂度都能保持在O(log
n)的水平。这种高效的性能使得AVL树在需要频繁进行动态修改操作的数据结构中非常有用,如数据库索引、文件系统等。然而,需要注意的是,AVL树在每次插入或删除操作后都需要进行平衡调整,这可能会增加一些额外的开销。但在大多数情况下,这种开销是可以接受的,因为AVL树提供了比未平衡的二叉搜索树更好的性能保证。

  • 22
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值