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;
	};

}
相关推荐
暮色_年华12 分钟前
Modern Effective C++item 9:优先考虑别名声明而非typedef
c++
重生之我是数学王子20 分钟前
QT基础 编码问题 定时器 事件 绘图事件 keyPressEvent QT5.12.3环境 C++实现
开发语言·c++·qt
CV学术叫叫兽33 分钟前
一站式学习:害虫识别与分类图像分割
学习·分类·数据挖掘
我们的五年44 分钟前
【Linux课程学习】:进程程序替换,execl,execv,execlp,execvp,execve,execle,execvpe函数
linux·c++·学习
一棵开花的树,枝芽无限靠近你1 小时前
【PPTist】添加PPT模版
前端·学习·编辑器·html
做人不要太理性1 小时前
【C++】深入哈希表核心:从改造到封装,解锁 unordered_set 与 unordered_map 的终极奥义!
c++·哈希算法·散列表·unordered_map·unordered_set
程序员-King.1 小时前
2、桥接模式
c++·桥接模式
chnming19871 小时前
STL关联式容器之map
开发语言·c++
VertexGeek1 小时前
Rust学习(八):异常处理和宏编程:
学习·算法·rust
程序伍六七2 小时前
day16
开发语言·c++