AVL树实现

目录

​编辑

一,AVL树的概念

二,实现AVL树(部分)

1.AVL树的节点

2.AVL数的插入

1.当根节点为nullptr时要执行如下代码:

2.当根节点不为nullptr时

1.当parent的_bf变为0时,parent之前的_bf的大小就是-1或者1如下:

2.当parent的平衡因子被更新为1或者-1时:

3.当parent的平衡因子更新后变成-2或者2时

三,一些其他函数

四,全部代码


一,AVL树的概念

首先我们得先知道AVL树是个啥,AVL树是个啥呢?百度一下你就知道:

在百度上的概念如上。根据AVL树的概念我们可以总结出AVL树有如下特点:

1.本身首先是一棵二叉搜索树。

2.带有平衡条件:每个结点的左右子树的高度之差的绝对值(平衡因子)最多为1。

二,实现AVL树(部分)

1.AVL树的节点

首先我们根据AVL树的特点可以想到的AVL树节点成员有:

1.因为AVL树还是一棵二叉树,所以AVL树中必有指针left与right,而且还有一个变量表示节点值。

2.因为二AVL树需要记录高度差,所以在节点内就会有一个变量来记录高度差,这个变量便是平衡因子。并且这个变量的类型是int。

3.最后为了方便后续操作,我们的节点内还会存放一个指针叫做parent,这个节点指向当前节点的父节点。

所以写出的节点代码如下:

cpp 复制代码
template <class K,class V>
	struct AVL_Node//节点
	{
		AVL_Node(pair<K,V>& key)
			:parent(nullptr)
			,_left(nullptr)
			,_right(nullptr)
			,_bf(0)
			,_key(key)
		{}

		AVL_Node<K, V>* parent;//父节点指针
		AVL_Node<K, V>* _left;//左孩子指针
		AVL_Node<K, V>* _right;//右孩子指针
		int _bf;//平衡因子
		pair<K, V>_key;//val
	};

2.AVL数的插入

AVL树的插入是我们这次实现的重头戏,在插入时要理解的重点内容便是:

1.插入时的旋转。

2.平衡因子的调整。

现在先来写在旋转前的代码:

**1.**当根节点为nullptr时要执行如下代码:
cpp 复制代码
if (_root == nullptr)//当根节点为空时
{
	_root = new AVL_Node<K, V>(key);
}
2.当根节点不为nullptr时

1.先找到插入位置:

cpp 复制代码
AVL_Node<K, V>* cur = _root;
AVL_Node<K, V>* parent = cur;
while (cur)
{
if (key < cur->_key)
{
  parent = cur;
  cur = cur->_left;
}
else if (key > cur->_key)
{
	parent = cur;
	cur = cur->_right;
}
else
{
	return false;
}
}						

这段寻找插入位置的代码和二叉搜索树的代码相同。

2.找到插入位置以后便开始进行插入

cpp 复制代码
cur = new AVL_Node<K, V>(key);
if (key.first > parent->_key.first)
{
	parent->_right = cur;
}
	else
{
  parent->_left = cur;
}

 cur->_parent = parent;//更新父节点

3.调整上一个节点的平衡因子。依据:_bf = 右节点的高度--左节点的高度

所以当我插入左边时:

cpp 复制代码
if (cur == parent->_left)//左边添加节点,parent的_bf减1
{
	parent->_bf--;
}

当我插入右边时:

cpp 复制代码
else if (cur == parent->_right)//右边添加节点,parntt的高度加1
{
  parent->_bf++;
}

4.根据父节点的_bf的大小来决定是否需要往上调整

1.当parent的_bf变为0时,parent之前的_bf的大小就是-1或者1如下:

其实我的parent变成0以后AVL树的高度下降,所以对我的其它节点的平衡因子的大小是没有什么影响的,所以在这种情况下我便可以直接退出:

cpp 复制代码
if (parent->_bf == 0)//子树高度不变不影响整棵树高度
{
  break;
}
2.当parent的平衡因子被更新为1或者-1时:

能变更新成这种情况,那我之前的平衡因子一定为0了。这时候,我整棵树的高度肯定是增加了,所以平衡因子必须改变所以代码如下:

cpp 复制代码
else if (parent->_bf == 1 || parent->_bf == -1)//子树高度变了,但是在接受范围内,节点的平衡因子要做调整
{
   cur = parent;
  parent = parent->_parent;
}

这段代码是不全的,这里只展示了节点向上调整的操作,如果想看整段代码请往后看。

3.当parent的平衡因子更新后变成-2或者2时

这个时候就发生异常了,这个时候就要开始旋转了。这个时候的旋转又分为四种:

1.单纯的右边高

如以下情况:

可以看到在插入新的节点以后,树的高度发生了变化并且都是右边的高度高且出现了异常情况。所以这个时候就要发生左旋来调整高度,示意图如下:

根据这个过程写出如下代码:

cpp 复制代码
void Leftuniflex(AVL_Node<K, V>* parent)
	{
        //记录要旋转的节点
		AVL_Node<K, V>* subR = parent->_right;
		AVL_Node<K, V>* subRL = subR->_left;

        //旋转
		parent->_right = subRL;
		subR->_left = parent;

		//更新父节点
		AVL_Node<K, V>* ppNode = parent->_parent;
		parent->_parent = subR;
		if (subRL)
			subRL->_parent = parent;

		if (parent == _root)
		{
			_root = subR;
		}
		 
		subR->_parent = ppNode;

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

因为在示意图里没有显示父节点的更新,所以在这里要注意对于父节点的更新。

2.单纯的左边高

如下情况:

看到这个示意图是否有一种似曾相识的感觉呢?其实单纯的左边高的旋转操作方法和单纯右边高的操作方法一样,只不过旋转的方向改变了。根据示意图写出代码如下:

cpp 复制代码
void Rightuniflex(AVL_Node<K, V>* parent)
	{
		//旋转
		AVL_Node<K, V>* subL = parent->_left;
		AVL_Node<K, V>* subLR = subL->_right;
		parent->_left = subLR;
		subL->_right = parent;

		//更新父节点
		AVL_Node<K, V>* ppNode = parent->_parent;
		parent->_parent = subL;
		if (subLR)
			subLR->_parent = parent;

		if (parent == _root)
		{
			_root = subL;
		}

		subL->_parent = ppNode;

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

3.右边整体高但是局部矮

解决方法:1.将整棵AVL树通过右单旋调整成单纯的右边高然后再将整棵树通过左单旋调整成平衡的AVL树。比如如以下情况:

一,新增节点插入到60的右节点b处,此时60的_bf为-1

在这种情况下,首先便要以90这个节点为parent进行右单旋让整棵树变成单纯的右边高。如下图所示:

在转化为单纯的右边高以后,便可以执行第二步了。第二步的操作便是和左旋操作是一样的,如图所示:

二,新增节点插入到60的右节点c处,此时60的_bf为1

画出示意图如下:

然后还是一样的操作将这棵树变成单纯的右边高:

然后继续执行第二步,也是单纯的左旋操作:

三,当h为0时,新增节点为60。此时60的_bf = 0

在这个时候还是一样的操作:先变成单纯的右边高

然后再操作:

再右边整体高,局部左边高的节点插入情况便是以上三种。其实这些情况的旋转操作都是一样的但是对应的平衡因子_bf的更新是不一样的。

根据以上情况写出代码如下:

cpp 复制代码
//右左双旋
	void RLuniflex(AVL_Node<K, V>* parent)
	{
		AVL_Node<K, V>* subR = parent->_right;
		AVL_Node<K, V>* subRL = subR->_left;
		int bf = subRL->_bf;

		Rightuniflex(subR);//先局部右旋
		Leftuniflex(parent);//再整体左旋


		//更新平衡因子
		if (bf == 0)//subRL自己就是新增
		{
			parent->_bf = subR->_bf = subRL->_bf = 0;
		}

		else if (bf == -1)
		{
			subR->_bf = 1;
			parent->_bf = subRL->_bf = 0;
		}

		else if (bf == 1)
		{
			parent->_bf = -1;
			subRL->_bf = subR->_bf = 0;
		}
		else
		{
			assert(false);
		}
		
	}

4.当左边整体高但是右边局部高时:

在这种情况下的分析方法和《当右边整体高但是左边局部高》的分析方法相同。也分为三步:

1.现局部左旋让AVL树变成单纯的左边高。

2.再整体右旋调整树的平衡。

3.调整平衡因子。

分析方法和上述代码相同,在这里就直接写代码了:

cpp 复制代码
//左右双旋(狗日的平衡因子,让我搞了半天,哭了)
	void LRuniflex(AVL_Node<K, V>* parent)
	{
		AVL_Node<K, V>* subL = parent->_left;
		AVL_Node<K, V>* subLR = subL->_right;
		int bf = subLR->_bf;

		Leftuniflex(subL);//先局部左旋
		Rightuniflex(parent);//再整体右旋


		//更新平衡因子
		if (bf == 0)//subRL自己就是新增
		{
			parent->_bf = subL->_bf = subLR->_bf = 0;
		}

		else if (bf == -1)
		{
			subL->_bf = 0;
			parent->_bf = 1;
		    subLR->_bf = 0;
		}

		else if (bf == 1)
		{
			parent->_bf = 0;
			subLR->_bf = 0;
			subL->_bf = -1;
		}
		else
		{
			assert(false);
		}

	}

兄弟们在写代码时千万不要和我一样粗心,找bug找了好久!!!!

三,一些其他函数

1.中序遍历函数

cpp 复制代码
void Inorder()
{
  _Inorder(_root);
}
cpp 复制代码
void _Inorder(AVL_Node<K, V>* root)
{
	if (root == nullptr)
	{
		return;
	}

  _Inorder(root->_left);
  cout << root->_key.first << " ";
 _Inorder(root->_right);
}

记住,AVL树的中序遍历也是一个有序的数列。

2.判断平衡的函数

cpp 复制代码
int Height(AVL_Node<K, V>* root)
		{
			if (root == nullptr)
			{
				return 0;
			}

			int L = Height(root->_left);
			int R = Height(root->_right);

			return L > R ? L + 1 : R + 1;
		}

		bool _Isblance(AVL_Node<K, V>* root)
		{
			if (root == nullptr)
			{
				return true;
			}

			int R = Height(root->_right);
			int L = Height(root->_left);
			int bf = root->_bf;

			if (R - L!=bf)
			{
				cout << "R-L:" << R - L << endl;
				cout << root->_key.first << "->" << root->_bf <<"平衡因子异常" << endl;
				return false;
			}

			return (abs(R-L)<2)&& _Isblance(root->_left) && _Isblance(root->_right);
			 
		}

四,全部代码

cpp 复制代码
#include<iostream>
#include<assert.h>
using namespace std;
using std::pair;

namespace AVL
{
	template <class K,class V>
	struct AVL_Node//节点
	{
		AVL_Node(pair<K,V>& key)
			:_parent(nullptr)
			,_left(nullptr)
			,_right(nullptr)
			,_bf(0)
			,_key(key)
		{}

		AVL_Node<K, V>* _parent;
		AVL_Node<K, V>* _left;
		AVL_Node<K, V>* _right;
		int _bf;
		pair<K, V>_key;
	};


	template <class K, class V>
	class AVL_Tree
	{
	public:
		bool insert( pair<K, V> key)
		{
			if (_root == nullptr)//当根节点为空时
			{
				_root = new AVL_Node<K, V>(key);
			}

			else//当根节点不为空时
			{
				AVL_Node<K, V>* cur = _root;
				AVL_Node<K, V>* parent = cur;
				while (cur)
				{
					if (key.first < cur->_key.first)
					{
						parent = cur;
						cur = cur->_left;
					}
					else if (key.first > cur->_key.first)
					{
						parent = cur;
						cur = cur->_right;
					}
					else
					{
						return false;
					}
				}

				cur = new AVL_Node<K, V>(key);
				if (key.first > parent->_key.first)
				{
					parent->_right = cur;
				 }
				else
				{
					parent->_left = cur;
				}

				cur->_parent = parent;

				while (parent)
				{
					if (cur == parent->_left)//左边添加节点,parent的高度减1
					{
						parent->_bf--;
					}
					else if (cur == parent->_right)//右边添加节点,parent的高度加1
					{
						parent->_bf++;
					}

					if (parent->_bf == 0)//子树高度不变不影响整棵树高度
					{
						break;
					}
					else if (parent->_bf == 1 || parent->_bf == -1)//子树高度变了,但是在接受范围内,节点的平衡因子要做调整
					{
						cur = cur->_parent;
						parent = parent->_parent;
					}
					else if (parent->_bf == 2 || parent->_bf == -2)
					{
						//Inorder();
						//出现异常该旋转了
						//单纯右边高,发生左单旋
						if (parent->_bf == 2 && cur->_bf == 1)
						{
							 Leftuniflex(parent);
						}
						//单纯左边高,发生右单旋
						else if (parent->_bf == -2 && cur->_bf == -1)
						{
							 Rightuniflex(parent);

						}
						//右边整体高,局部矮
						else if (parent->_bf == 2 && cur->_bf == -1)
						{
							RLuniflex(parent);
						}
						//左边整体高,局部矮
						else if (parent->_bf == -2 && cur->_bf == 1)
						{
							LRuniflex(parent);
						}
					    
						else
						{
							assert(false);
						}
						break;
					}

					else
					{
						assert(false);
					}
			
				}
			}
				return true;
	    }

	void Leftuniflex(AVL_Node<K, V>* parent)
	{
		AVL_Node<K, V>* subR = parent->_right;
		AVL_Node<K, V>* subRL = subR->_left;
		parent->_right = subRL;
		subR->_left = parent;

		//更新父节点
		AVL_Node<K, V>* ppNode = parent->_parent;
		parent->_parent = subR;
		if (subRL)
			subRL->_parent = parent;

		if (parent == _root)
		{
			_root = subR;
		}
		 
		else
		{
			if (parent == ppNode->_left)
			{
				ppNode->_left = subR;
			}
			else
			{
				ppNode->_right = subR;
			}
		}
		subR->_parent = ppNode;

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


	void Rightuniflex(AVL_Node<K, V>* parent)
	{
		//旋转
		AVL_Node<K, V>* subL = parent->_left;
		AVL_Node<K, V>* subLR = subL->_right;
		parent->_left = subLR;
		subL->_right = parent;

		//更新父节点
		AVL_Node<K, V>* ppNode = parent->_parent;
		parent->_parent = subL;
		if (subLR)
			subLR->_parent = parent;

		if (parent == _root)
		{
			_root = subL;
		}

		else
		{
			if (parent == ppNode->_left)
			{
				ppNode->_left = subL;
			}
			else
			{
				ppNode->_right = subL;
			}
		}
		subL->_parent = ppNode;

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

	//右左双旋
	void RLuniflex(AVL_Node<K, V>* parent)
	{
		AVL_Node<K, V>* subR = parent->_right;
		AVL_Node<K, V>* subRL = subR->_left;
		int bf = subRL->_bf;

		Rightuniflex(subR);//先局部右旋
		Leftuniflex(parent);//再整体左旋


		//更新平衡因子
		if (bf == 0)//subRL自己就是新增
		{
			parent->_bf = subR->_bf = subRL->_bf = 0;
		}

		else if (bf == -1)
		{
			subR->_bf = 1;
			parent->_bf = subRL->_bf = 0;
		}

		else if (bf == 1)
		{
			parent->_bf = -1;
			subRL->_bf = subR->_bf = 0;
		}
		else
		{
			assert(false);
		}
		
	}

	//左右双旋(狗日的平衡因子,让我搞了半天,哭了)
	void LRuniflex(AVL_Node<K, V>* parent)
	{
		AVL_Node<K, V>* subL = parent->_left;
		AVL_Node<K, V>* subLR = subL->_right;
		int bf = subLR->_bf;

		Leftuniflex(subL);//先局部左旋
		Rightuniflex(parent);//再整体右旋


		//更新平衡因子
		if (bf == 0)//subRL自己就是新增
		{
			parent->_bf = subL->_bf = subLR->_bf = 0;
		}

		else if (bf == -1)
		{
			subL->_bf = 0;
			parent->_bf = 1;
		    subLR->_bf = 0;
		}

		else if (bf == 1)
		{
			parent->_bf = 0;
			subLR->_bf = 0;
			subL->_bf = -1;
		}
		else
		{
			assert(false);
		}

	}


	void Inorder()
	{
		_Inorder(_root);
	}

	void Isblance()
	{
		_Isblance(_root);
	}
	 


	private:

		void _Inorder(AVL_Node<K, V>* root)
		{
			if (root == nullptr)
			{
				return;
			}

			_Inorder(root->_left);
			cout << root->_key.first << " ";
			_Inorder(root->_right);
		}

		int Height(AVL_Node<K, V>* root)
		{
			if (root == nullptr)
			{
				return 0;
			}

			int L = Height(root->_left);
			int R = Height(root->_right);

			return L > R ? L + 1 : R + 1;
		}

		bool _Isblance(AVL_Node<K, V>* root)
		{
			if (root == nullptr)
			{
				return true;
			}

			int R = Height(root->_right);
			int L = Height(root->_left);
			int bf = root->_bf;

			if (R - L!=bf)
			{
				cout << "R-L:" << R - L << endl;
				cout << root->_key.first << "->" << root->_bf <<"平衡因子异常" << endl;
				return false;
			}

			return (abs(R-L)<2)&& _Isblance(root->_left) && _Isblance(root->_right);
			 
		}

		AVL_Node<K, V>* _root = nullptr;
	};

}
相关推荐
爱吃西瓜的小菜鸡3 小时前
【C语言】判断回文
c语言·学习·算法
别NULL3 小时前
机试题——疯长的草
数据结构·c++·算法
小A1593 小时前
STM32完全学习——SPI接口的FLASH(DMA模式)
stm32·嵌入式硬件·学习
岁岁岁平安3 小时前
spring学习(spring-DI(字符串或对象引用注入、集合注入)(XML配置))
java·学习·spring·依赖注入·集合注入·基本数据类型注入·引用数据类型注入
武昌库里写JAVA3 小时前
Java成长之路(一)--SpringBoot基础学习--SpringBoot代码测试
java·开发语言·spring boot·学习·课程设计
qq_589568103 小时前
数据可视化echarts学习笔记
学习·信息可视化·echarts
CYBEREXP20084 小时前
MacOS M3源代码编译Qt6.8.1
c++·qt·macos
yuanbenshidiaos4 小时前
c++------------------函数
开发语言·c++
yuanbenshidiaos4 小时前
C++----------函数的调用机制
java·c++·算法
兔C4 小时前
微信小程序的轮播图学习报告
学习·微信小程序·小程序