C++|AVL树(模拟实现,测试)

目录

一、概念

二、AVL树的实现

2.1节点定义

2.2AVL树的基础架构

2.3AVL树的插入 

2.3.1按照二叉搜索树的方式插入新节点

2.3.2调整节点的平衡因子

2.3.2.1 新节点插入较高右子树的右侧:进行左单旋

 2.3.2.2新结点插入较高左子树的左侧:进行右单旋

2.3.2.3新节点插入较高左子树的右侧:先左单旋再右单旋

2.3.2.4新结点插入较高右子树的左侧:先右单旋再左单旋

2.3.2.5调节平衡因子(完整版插入)

2.4AVL树的性能分析

三、AVL树的测试


一、概念

AVL树和红黑树都可以用来作为树形关联式容器的底层实现。但最终采用的是红黑树,虽然他们在时间复杂度上是一样的,但在某些场景下,红黑树更通用。他们依然是二叉搜索树,只不过实现二叉搜索树的方式不同,而被称做平衡二叉搜索树。

AVL树是由两名数学家(G.M.Adelson-Velskii和E.M.Landis)发明出来的一种解决二叉搜索树缺陷的方法,该树的名称也是这两位科学家的简称而得。

AVL树解决问题概念:当向二叉搜索树中插入新结点后,如果能保证每个节点的左右子树高度之差的绝对值不超过1(若超过,需要进行调整,待后续讲解),即可降低树的高度,从而减少平均搜索长度。

AVL树性质:

  • 他的左右子树都是AVL树
  • 左右子树高度之差的绝对值不超过1(-1/0/1),该高度之差也就是平衡因子

平衡因子= 右子树高度-左子树高度,也可以左减右,没有明确规定,那我采用的是右减左。

注意:是用高度之差计算出平衡因子,不是用平衡因子计算出平衡因子。其次平衡因子并不是必须的,他只是帮助我们更便捷的控制树

思考:既然了解了AVL树的规则,那为什么其高度差是不超过1,而不是0?

因为节点是一个一个插入的,有些情况无法做到左右子树高度相等,比如,2个节点的树,4个节点的树

更全面的平衡二叉搜索树如图:平衡因子记录高度差 

AVL树的高度是平衡的,高度可保持在log n,搜索时间复杂度为O(log N)。

二、AVL树的实现

2.1节点定义

节点中包括左指针,右指针,指向父节点的指针,平衡因子,要存储的数据值。 

//节点
	template<class T>
	struct AVLTreeNode
	{
		AVLTreeNode(const T& data = T())
			:_left(nullptr)
			,_right(nullptr)
			,_parent(nullptr)
			,_bf(0)
			,_data(data)
		{

		}
		AVLTreeNode<T>* _left;
		AVLTreeNode<T>* _right;
		AVLTreeNode<T>* _parent;
		T _bf;//平衡因子
		T _data;//要存储的值
	};

2.2AVL树的基础架构

    template<class T>
	class AVLTree
	{
		typedef AVLTreeNode<T> Node;
		typedef Node* PNode;
	public:
		AVLTree()
			:_Root(nullptr)
		{}

	private:
		PNode _Root;
	};

2.3AVL树的插入 

 AVL树的就是在二叉搜索树的基础上引入了平衡因子,因此AVL树也可以看成是二叉搜索树。那么AVL树的插入过程可以分为两步:

1.按照二叉搜索树的方式插入新节点

2.调整节点的平衡因子

2.3.1按照二叉搜索树的方式插入新节点


		bool Insert(const T& data)
		{
			if (_Root == nullptr)
			{
				_Root = new Node(data);
				return true;
			}
			PNode cur = _Root;
			PNode parent = nullptr;
			//找插入点
			while (cur)
			{
				if (data < cur->_data)
				{
					parent = cur;
					cur = cur->_left;
					
				}
				else if (data > cur->_data)
				{
					parent = cur;
					cur = cur->_right;
	
				}
				else
					return false;
			}
			//插入
			PNode node = new Node(data);
			if (data < parent->_data)
			{
				parent->_left = node;
			}
			else
			{
				parent->_right = node;
			}
			node->_parent = parent;
            return ture;
        }

2.3.2调整节点的平衡因子

这里就有点漫长了,首先来了解一点,当我们插入节点时,会影响哪些节点的平衡因子?

会影响祖先的平衡因子,有时候是部分的,有时候是全部。至于为什么是这样,这就要来了解平衡因子的更新原则,如图:

 由上图可知根据节点的插入位置,来决定哪些祖先的平衡因子需要改变。

那么现在就要来重点讲一讲旋转问题了,当发生不平衡时,根据节点插入位置的不同,AVL树的旋转分为四种:

2.3.2.1 新节点插入较高右子树的右侧:进行左单旋

对于其插入,可以通过抽象图来做代表,通过具象图来进行更好的分析。 

 ​​​​​​​

 

根据图的分析,我们可将他转化为代码,其中要注意的是,当旋转完后,也要更新节点中指向父 节点的指针:

        //左单旋
		void RotateL(PNode parent)
		{
			PNode subR = parent->_right;
			PNode subRL = subR->_left;
			PNode pparent = parent->_parent;

			if (parent == _Root)//更新根节点
			{
				_Root = subR;
			}
			else
			{
				//更新parent的父节点指向
				if (parent == pparent->_left)
				{
					subR->_parent = pparent;
					pparent->_left = subR;
				}
				else
				{
					subR->_parent = pparent;
					pparent->_right = subR;
				}
			}

			//parent的右指针指向subRL,subRL的父节点指向parent
			parent->_right = subR->_left;
			if(subRL)//subR的左节点可能不存在
				subRL->_parent = parent;

			//subR的左指针指向parent,parent的父节点指向subR
			subR->_left = parent;
			parent->_parent = subR;

			//更新平衡因子
			parent->_bf = subR->_bf = 0;
		}
 2.3.2.2新结点插入较高左子树的左侧:进行右单旋

 

根据图的分析,我们可将他转化为代码:

        //右单旋
		void RotateR(PNode parent)
		{
			PNode subL = parent->_left;
			PNode subLR = subL->_right;
			PNode pparent = parent->_parent;

			if (_Root == parent)
			{
				_Root = subL;
				subL->_parent = nullptr;
			}
			else
			{
				//更新parent的父节点指向
				if (pparent->_left == parent)
				{
					pparent->_left = subL;
				}
				else
				{
					pparent->_right = subL;
				}
				subL->_parent = pparent;
			}
			//parent的左指针指向subLR,subLR的父节点指向parent
			parent->_left = subLR;
			if(subLR)//subR的右节点可能不存在
				subLR->_parent = parent;
			//subL的右指针指向parent,parent的父节点指向subL
			subL->_right = parent;
			parent->_parent = subL;

			//更新平衡因子
			parent->_bf = subL->_bf = 0;
		}
2.3.2.3新节点插入较高左子树的右侧:先左单旋再右单旋

有了上述的抽象图了解,这里就不在进行具象图分析,而是直接在抽象图上分析。 

当在右侧插入时,其也分为两种情况,为什么要区分这两种情况?

因为他们影响平衡因子的情况不同,所以要做区分。

 

 

 将上述情况转换为代码:

	    //左右单旋
		void RotateLR(PNode parent)
		{
			PNode subL = parent->_left;
			PNode subLR = subL->_right;
			int bf = subLR->_bf;//记录下来,为了后面更新平衡因子

			RotateL(parent->_left);//左单旋
			RotateR(parent);//右单旋

			因为左单旋和右单旋已经破坏了原本的平衡因子,所以需要手动更新
			//那么这里以原本subRL的平衡因子来做衡量
			if (bf == -1)
			{
				parent->_bf = 1;
				subL->_bf = 0;
				subLR->_bf = 0;
			}
			else if (bf == 1)
			{
				parent->_bf = 0;
				subL->_bf = -1;
				subLR->_bf = 0;
			}
			else if (bf == 0)//表示新插入节点
			{
				parent->_bf = subL->_bf = subLR->_bf = 0;
			}
		}
2.3.2.4新结点插入较高右子树的左侧:先右单旋再左单旋

 

 将上述情况转换为代码:

        //右左单旋
		void RotateRL(PNode parent)
		{
			PNode subR = parent->_right;
			PNode subRL = subR->_left;
			int bf = subRL->_bf;//记录下来,为了后面更新平衡因子
			RotateR(parent->_right);//右单旋
			RotateL(parent);//左单旋

			//因为右单旋和左单旋已经破坏了原本的平衡因子,所以需要手动更新
			//那么这里以原本subRL的平衡因子来做衡量
			if (bf == 1)
			{
				parent->_bf = -1;
				subR->_bf = 0;
				subRL->_bf = 0;
			}
			else if (bf == -1)
			{
				parent->_bf = 0;
				subR->_bf = 1;
				subRL->_bf = 0;
			}
			else if (bf == 0)//表示新插入节点
			{
				parent->_bf = subR->_bf = subRL->_bf = 0;
			}
		}
2.3.2.5调节平衡因子(完整版插入)

当插入新结点时,更新祖先结点的平衡因子,并一直往上判断,根据祖先结点的平衡因子判断是否需要进行旋转。

        bool Insert(const T& data)
		{
			if (_Root == nullptr)
			{
				_Root = new Node(data);
				return true;
			}
			PNode cur = _Root;
			PNode parent = nullptr;
			//找插入点
			while (cur)
			{
				if (data < cur->_data)
				{
					parent = cur;
					cur = cur->_left;
					
				}
				else if (data > cur->_data)
				{
					parent = cur;
					cur = cur->_right;
	
				}
				else
					return false;
			}
			//插入
			PNode node = new Node(data);
			if (data < parent->_data)
			{
				parent->_left = node;
			}
			else
			{
				parent->_right = node;
			}
			node->_parent = parent;
			cur = node;


			while (cur)
			{
				if (parent == nullptr)
					break;
				//更新平衡因子
				if (cur == parent->_left)
					parent->_bf--;
				else
					parent->_bf++;

				if (parent->_bf == 0)//不会影响爷爷,直接插入结束
				{
					break;
				}
				else if(parent->_bf == 1 || parent->_bf == -1)//继续往上
				{
					cur = parent;
					parent = parent->_parent;
				}
				else if(parent->_bf == 2 || parent->_bf == -2)
				{
					if (cur == parent->_right)
					{
						if (cur->_bf == 1)
						{
							//左单旋
							RotateL(parent);
						}
						else if (cur->_bf == -1)
						{
							//右左单旋
							RotateRL(parent);
						}
					}
					else
					{
						if (cur->_bf == 1)
						{
							//左右单旋
							RotateLR(parent);
						
						}
						else if (cur->_bf == -1)
						{
							//右单旋
							RotateR(parent);
						}
					}	
					break;
				}
				else
				{
					assert(false);//插入之前就有问题
				}
			}	
			return true;

		}

因为AVL树也是二叉搜索树,可按照二叉搜索树的方式将结点删除,然后更新平衡因子,只不过,删除节点后的平衡因子更新,最差情况下一直要调整到根节点的位置。

对于AVL树的删除,在这里就不在模拟实现了,因为有了插入的了解,就足够我们进行对AVL树的学习认识。

当然,如果想要进一步了解,可参考<<算法导论>>或<<数据结果-用面向南对象方法与C++描述>>殷人昆版。 

2.4AVL树的性能分析

 AVL树是一颗绝对平衡的二叉搜索树,弥补了普通版本的二叉搜索树的缺陷。其要求每个节点的左右子树高度差的绝对值不超过1,这样就可以保证查询时高效的时间复杂度,即log N。但是如果要

对AVL树做一些结构修改的操作,性能非常低下,

比如:插入时要维护其绝对平衡,旋转的次数比较多,更差的是在删除时,有可能一直要让旋转持续到根的位置。

因此 :如果需要一种查询高效且有序的数据结构,而且数据的个数为静态的(即不会变),可以考虑AVL树,但一个结构经常修改,就不太适合。

三、AVL树的测试

 

//AVLTree.h
#include <iostream>
#include <vector>
#include <assert.h>
using namespace std;

namespace bit
{
	//节点
	template<class T>
	struct AVLTreeNode
	{
		AVLTreeNode(const T& data = T())
			:_left(nullptr)
			,_right(nullptr)
			,_parent(nullptr)
			,_bf(0)
			,_data(data)
		{}
		AVLTreeNode<T>* _left;
		AVLTreeNode<T>* _right;
		AVLTreeNode<T>* _parent;
		T _bf;//平衡因子
		T _data;
	};


	template<class T>
	class AVLTree
	{
		typedef AVLTreeNode<T> Node;
		typedef Node* PNode;
	public:
		AVLTree()
			:_Root(nullptr)
		{}

		bool Insert(const T& data)
		{
			if (_Root == nullptr)
			{
				_Root = new Node(data);
				return true;
			}
			PNode cur = _Root;
			PNode parent = nullptr;
			//找插入点
			while (cur)
			{
				if (data < cur->_data)
				{
					parent = cur;
					cur = cur->_left;
					
				}
				else if (data > cur->_data)
				{
					parent = cur;
					cur = cur->_right;
	
				}
				else
					return false;
			}
			//插入
			PNode node = new Node(data);
			if (data < parent->_data)
			{
				parent->_left = node;
			}
			else
			{
				parent->_right = node;
			}
			node->_parent = parent;
			cur = node;


			while (cur)
			{
				if (parent == nullptr)
					break;
				//更新平衡因子
				if (cur == parent->_left)
					parent->_bf--;
				else
					parent->_bf++;

				if (parent->_bf == 0)//不会影响爷爷,直接插入结束
				{
					break;
				}
				else if(parent->_bf == 1 || parent->_bf == -1)//继续往上
				{
					cur = parent;
					parent = parent->_parent;
				}
				else if(parent->_bf == 2 || parent->_bf == -2)
				{
					if (cur == parent->_right)
					{
						if (cur->_bf == 1)
						{
							//左单旋
							RotateL(parent);
						}
						else if (cur->_bf == -1)
						{
							//右左单旋
							RotateRL(parent);
						}
					}
					else
					{
						if (cur->_bf == 1)
						{
							//左右单旋
							RotateLR(parent);
						
						}
						else if (cur->_bf == -1)
						{
							//右单旋
							RotateR(parent);
						}
					}	
					break;
				}
				else
				{
					assert(false);//插入之前就有问题
				}
			}	
			return true;

		}

		//左单旋
		void RotateL(PNode parent)
		{
			PNode subR = parent->_right;
			PNode subRL = subR->_left;
			PNode pparent = parent->_parent;

			if (parent == _Root)//更新根节点
			{
				_Root = subR;
				subR->_parent = nullptr;
			}
			else
			{
				//更新parent的父节点指向
				if (parent == pparent->_left)
				{
					pparent->_left = subR;
				}
				else
				{
					pparent->_right = subR;
				}
				subR->_parent = pparent;

			}

			//parent的右指针指向subRL,subRL的父节点指向parent
			parent->_right = subR->_left;
			if(subRL)//subR的左节点可能不存在
				subRL->_parent = parent;

			//subR的左指针指向parent,parent的父节点指向subR
			subR->_left = parent;
			parent->_parent = subR;

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

		//右单旋
		void RotateR(PNode parent)
		{
			PNode subL = parent->_left;
			PNode subLR = subL->_right;
			PNode pparent = parent->_parent;

			if (_Root == parent)
			{
				_Root = subL;
				subL->_parent = nullptr;
			}
			else
			{
				//更新parent的父节点指向
				if (pparent->_left == parent)
				{
					pparent->_left = subL;
				}
				else
				{
					pparent->_right = subL;
				}
				subL->_parent = pparent;
			}
			//parent的左指针指向subLR,subLR的父节点指向parent
			parent->_left = subLR;
			if(subLR)//subR的右节点可能不存在
				subLR->_parent = parent;
			//subL的右指针指向parent,parent的父节点指向subL
			subL->_right = parent;
			parent->_parent = subL;

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

		//左右单旋
		void RotateLR(PNode parent)
		{
			PNode subL = parent->_left;
			PNode subLR = subL->_right;
			int bf = subLR->_bf;//记录下来,为了后面更新平衡因子

			RotateL(parent->_left);//左单旋
			RotateR(parent);//右单旋

			因为左单旋和右单旋已经破坏了原本的平衡因子,所以需要手动更新
			//那么这里以原本subRL的平衡因子来做衡量
			if (bf == -1)
			{
				parent->_bf = 1;
				subL->_bf = 0;
				subLR->_bf = 0;
			}
			else if (bf == 1)
			{
				parent->_bf = 0;
				subL->_bf = -1;
				subLR->_bf = 0;
			}
			else if (bf == 0)//表示新插入节点
			{
				parent->_bf = subL->_bf = subLR->_bf = 0;
			}
		}

		//右左单旋
		void RotateRL(PNode parent)
		{
			PNode subR = parent->_right;
			PNode subRL = subR->_left;
			int bf = subRL->_bf;//记录下来,为了后面更新平衡因子
			RotateR(parent->_right);//右单旋
			RotateL(parent);//左单旋

			//因为右单旋和左单旋已经破坏了原本的平衡因子,所以需要手动更新
			//那么这里以原本subRL的平衡因子来做衡量
			if (bf == 1)
			{
				parent->_bf = -1;
				subR->_bf = 0;
				subRL->_bf = 0;
			}
			else if (bf == -1)
			{
				parent->_bf = 0;
				subR->_bf = 1;
				subRL->_bf = 0;
			}
			else if (bf == 0)//表示新插入节点
			{
				parent->_bf = subR->_bf = subRL->_bf = 0;
			}
		}

		void InOrder()
		{
			_InOrder(_Root);
			cout << endl;
		}
		int Height()
		{
			return _Height(_Root);
		}
		int _Height(PNode Root)
		{
			if (Root == nullptr)
				return 0;
			int _LeftHeight = _Height(Root->_left)+ 1;
			int _RightHeight = _Height(Root->_right) + 1;
			return max(_LeftHeight, _RightHeight);
		}
		bool _IsBalance()
		{
			return _IsBalanceTree(_Root);
		}
		bool _IsBalanceTree(PNode Root)
		{
			if (Root == nullptr)
				return true;
			int _LeftHeight = _Height(Root->_left);
			int _RightHeight = _Height(Root->_right);
			int sub =_RightHeight - _LeftHeight ;
			if (sub != Root->_bf)
			{
				cout << Root->_data << ":" << "平衡因子异常" << endl;
				return false;
			}

			if (sub == 1 || sub == -1 || sub == 0)
			{
				return _IsBalanceTree(Root->_left) && _IsBalanceTree(Root->_right);
			}
			else
				return false;

		}

		PNode Find(size_t data)
		{
			PNode cur = _Root;
			while (cur)
			{
				if (cur->_data < data)
				{
					cur = cur->_left;
				}
				else if (cur->_data > data)
				{
					cur = cur->_right;
				}
				else
				{
					return cur;
				}
			}

			return NULL;
		}


		size_t Size()
		{
			return _Size(_Root);
		}
		size_t _Size(PNode cur)
		{
			if (cur == nullptr)
				return 0;

			return _Size(cur->_left) + _Size(cur->_right) + 1;
		}
		
	private:
		void _InOrder(PNode Root)
		{
			if (Root == nullptr)
				return;

			_InOrder(Root->_left);
			cout << Root->_data<< endl;
			_InOrder(Root->_right);
		}
		PNode _Root;
	};
	void AVLTreeTest()
	{
		//int arr[] = { 16,3,7,11,9,26,18,14,15};
		int arr[] = { 4,2,6,1,3,5,15,7,16,14 };
		AVLTree<int> avl;

		for (auto e : arr)
		{

			avl.Insert(e);
		}
		avl.InOrder();
		cout <<avl._IsBalance();
	}
	void TestAVLTree2()
	{
		const int N = 9;
		vector<int> v;
		v.reserve(N);
		srand(time(0));

		for (size_t i = 0; i < N; i++)
		{
			v.push_back(rand() + i);
			//cout << v.back() << endl;
		}

		size_t begin2 = clock();
		AVLTree<int> t;
		for (auto e : v)
		{
			t.Insert(e);
			cout << "Insert:" << e << "->" << t._IsBalance() << endl;
		}
		size_t end2 = clock();

		cout << "Insert:" << end2 - begin2 << endl;

		cout << t._IsBalance() << endl;

		cout << "Height:" << t.Height() << endl;
		cout << "Size:" << t.Size() << endl;

		size_t begin1 = clock();
		// 确定在的值
		for (auto e : v)
		{
			t.Find(e);
		}

		// 随机值
		for (size_t i = 0; i < N; i++)
		{
			t.Find((rand() + i));
		}

		size_t end1 = clock();

		cout << "Find:" << end1 - begin1 << endl;
	}
}
//test.cpp
#include "AVLTree.h"

int main()
{
	bit::AVLTreeTest();
	//bit::TestAVLTree2();

	return 0;
}

输出结果:

//test.cpp
#include "AVLTree.h"

int main()
{
	//bit::AVLTreeTest();
	bit::TestAVLTree2();

	return 0;
}

 输出结果:

end~ 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值