带你手撕AVL树 平衡搜索树! c++ (附原码)

目录

不会就画图!不会就画图!不会就画图!

一、AVL树

二、旋转

一、分析

二、旋转处理

1、右单旋

2、左单旋

3、双旋

右左旋

左右旋

三、原码


一、AVL树

二叉搜索树如果接近有序,就接近单只,查找效率极低

所以,为了解决这个缺点,控制平衡,所以推出了AVL树
1962年,两位俄罗斯数学家搞出来的,用名字首字母命名
AVL树也叫二叉搜索平衡树
特点:左右子树都是AVL树,左右子树高度差绝对值不超过1(也叫平衡因子)
平衡因子不一定是1,这只是一种实现方案
为什么不是相等,而是不超过1?因为做不到,例如2/4个节点,就不行

二、旋转

一、分析

1、按照搜索树的规则插入
2、更新插入节点的祖先节点的平衡因子
1)插入父亲的左边,父亲平衡因子--
2)插入父亲的左边,父亲平衡因子--
此时,父亲节点平衡因子变化,有三种情况:
a、父亲平衡因子==0,父亲所在子树高度不变,不必往上更新,插入结束
b、父亲平衡因子==1/-1,父亲所在子树高度变化,需要往上更新祖先平衡因子
c、父亲平衡因子==2/-2,父亲所在子树已经不平衡,需要旋转处理

以上情况,为所有变化情况
变化后的平衡因子只可能会出现0/1//-1-2/2的情况
不可能出现其他情况
为什么?
有兴趣的可以看以下过程

插入左边,父亲平衡因子--;插入右边,父亲平衡因子++(默认右边-左边为平衡因子)
如果插入后,父亲节点变为0
说明两种情况:
1、原来为1,插入左边,--,变为0,依旧平衡
2,原来为-1,插入右边,++,变为0,依旧平衡
不用继续往上更新,插入把矮的那边填上了

如果插入后,父亲节点变为1/-1
也说明两种情况:
1、原来为0,插入左边,--,变为-1,高度变化为-1
2,原来为0,插入右边,++,变为1,高度变化为1
需要继续往上更新,因为以父节点所在的子树的高度已经发生了变化
进而影响了上面的平衡

因为,插入前的AVL树本身就是一个平衡树,平衡因子都是0/-1/1
插入后高度变化,要么左边多一个,要么右边多一个
左边多一个,父节点--;右边多一个,父亲节点++
所以,就只可能在0/1/-1的基础上++/--
所以,结果只可能是0/1/-1/2/-2
因此,
高度变为1/-1,一定是从0++/--变来的
如果高度为-2/2,就说明当前树不平衡了,需要旋转调整

那么,什么时候需要调整呢?
当父节点的平衡因子出现2/-2的时候,就不符合AVL树平衡,需调整
如果父亲平衡因子为0,不必继续往上更新,因为不影响上面的平衡

二、旋转处理

这里需要用到左单旋、右单旋的概念

什么时候左旋?什么时候右旋?
左边高了,右单旋;右边高了,左单旋
具体是什么样子呢?

插入左边/右边,更新父节点的平衡因子--/++
然后一直往上更新祖先节点的平衡因子,直到为空
期间,如果遇到某一个祖先节点平衡因子变为2/-2,旋转

当parent为-2时,说明左子树高度高,插入的左子树,此时cur必定为-1;需要右旋
当parent为2时,说明右子树高度高,插入的右子树,此时cur必定为1,需要左旋
对应的情况,把parent节点作为参数,传入对应的左旋和右旋的逻辑函数实现

(相对命名法,相对于某个对象命名)
parent、subL(相对parent的left)、subLR(相对subL的R)


注意:
subRL可能为空
parent可能为根节点,也可能不是根节点

1、右单旋

2、左单旋:


3、双旋:2 -1/ -2 1

右左旋

左右旋

不是单纯的左边高、右边高


三、原码

#pragma once
#pragma once
#include<iostream>
#include<assert.h>
#include<vector>
#include<time.h>

using namespace std;

namespace myspace
{
	template<class K, class V >
	struct AVLreeNode
	{
		AVLreeNode<K, V>* _left;
		AVLreeNode<K, V>* _right;
		AVLreeNode<K, V>* _parent;
		pair<K, V> _kv;
		int _bf;//balence factor

		AVLreeNode(const pair<K,V>& kv)
			:_left(nullptr)
			, _right(nullptr)
			, _parent(nullptr)
			,_kv(kv)
			,_bf(0)
		{
		}
	};

	template<class K, class V>
	class AVLTree
	{
		typedef AVLreeNode<K, V> Node;
	public:


	

		//插入
		bool Insert(const pair<K,V>& kv)
		{
			if (_root == nullptr)
			{
				_root = new Node(kv);
				return true;
			}

			Node* cur = _root;
			Node* parent = nullptr;

			while (cur)
			{

				if (kv.first < cur->_kv.first)
				{
					parent = cur;
					cur = cur->_left;
				}
				else if (kv.first > cur->_kv.first)
				{
					parent = cur;
					cur = cur->_right;
				}
				else//找到相等key
				{
					return false;
				}

			}

			cur = new Node(kv);

				if (kv.first < parent->_kv.first )//插入左
				{
					parent->_left =cur;
				}
				else //插入右
				{
					parent->_right = cur;
				}

				cur->_parent = parent;
				
				//插入完毕
				//往上更新祖先平衡因子
				while (parent)
				{
					if (parent->_left == cur)
					{
						parent->_bf--;
					}
					else
					{
						parent->_bf++;
					}

					if (parent->_bf == 0)
					{
						break;
					}
					else if(parent->_bf == 1 || parent->_bf == -1)//-1/1
					{
						//继续向上更新
						cur = parent;
						parent = parent->_parent;

					}
					else if (parent->_bf == 2 || parent->_bf == -2)//2/-2
					{
						//旋转处理
						if (parent->_bf == -2 && cur->_bf == -1)
						{
							//右旋
							RotateR(parent);

						}
						else if (parent->_bf == -2 && cur->_bf == 1)
						{
							//双旋
							RotateLR(parent);

						}
						else if (parent->_bf == 2 && cur->_bf == 1)
						{
							//左旋

							RotateL(parent);
						}
						else if (parent->_bf == 2 && cur->_bf == -1)
						{
							//双旋转
							RotateRL(parent);
						}

						break;
					}
					else
					{
						return false;
					}

				}

			return true;
		}

		//右旋
		void RotateR( Node* parent)
		{
			//此时parent已经为-2,cur为-1
			Node* subL = parent->_left;
			Node* subLR = subL->_right;

			parent->_left = subLR;
			if (subLR)//subLR可能为空
			{
				subLR->_parent = parent;
			}

			subL->_right = parent;
			Node* ppNode = parent->_parent;
			parent->_parent = subL;

			//注意修改顺序
			if (parent == _root)
			{
				_root = subL;
				_root->_parent = nullptr;
			}
			else
			{
				if (ppNode->_left == parent)
				{
					ppNode->_left = subL;
				}
				else
				{
					ppNode->_right = subL;
				}
				subL->_parent = ppNode;
			}

			subL->_bf = parent->_bf  = 0;

		}

		//左旋
		void RotateL( Node* parent)
		{
			//此时parent已经为2,cur为1
			Node* subR = parent->_right;
			Node* subRL = subR->_left;


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

			subR->_left = parent;

			Node* ppNode = parent->_parent;
			parent->_parent = subR;

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


			subR->_bf = parent->_bf  = 0;

		}

		//右左旋
		void RotateRL( Node* parent)
		{
			//对折线型插入,进行判断控制
			//插入之后,进行控制

			Node* subR = parent->_right;
			Node* subRL = subR->_left;
			
			int bf = subRL->_bf;

			RotateR(subR);
			RotateL(parent);

			subRL->_bf = 0;
			if (bf == 1)//右边插入,右节点给了右边
			{
				subR->_bf = 0;
				parent->_bf = 0;
			}
			else if (bf == -1)//左边插入
			{
				subR->_bf = 0;
				parent->_bf = 1;
			}
			else //字节本身,只有三个节点情况
			{
				subR->_bf = 0;
				parent->_bf = 0;
			}
			
		}

		//左右旋
		void RotateLR( Node* parent)
		{
			Node* subL = parent->_left;
			Node* subLR = subL->_right;
			
			int bf = subLR->_bf;

			RotateL(subL);
			RotateR(parent);

			subLR->_bf = 0;
			if (bf == 1)//右边插入
			{
				subL->_bf = 0;
				parent->_bf = -1;
			}
			else if (bf == -1)//插入左边
			{
				subL->_bf = 1;
				parent->_bf = 0;
			}
			else 
			{
				subL->_bf = 0;
				parent->_bf = 0;
			}
			
		}
		
		//查找
		Node* find(const K& key)
		{

			Node* parent = nullptr;
			Node* cur = _root;

			while (cur)
			{
				if (cur->_kv.first == key)
				{
					return cur;
				}

				if (key < cur->_kv.first)
				{
					parent = cur;
					cur = cur->_left;
				}
				else
				{
					parent = cur;
					cur = cur->_right;
				}
			}

			//走到这,没有找到
			return nullptr;

		}

		//中序
		void Inorder()
		{
			_Inorder(_root);
			cout << endl;
		}

		int size()
		{
			return  _size(_root);
		}

		bool isBalance()
		{
			return _is_balance(_root);
		}

		int Height()
		{
			return _Height(_root);
		}

	private:

		bool _is_balance(Node* root)
		{

			if (root == nullptr)
			{
				return true;
			}
			//判断平衡:左右两树相减
			//求得每一棵树的左右子树高度差
			int leftHight = _Height(root->_left);
			int rightHeight = _Height(root->_right);

			if (abs( leftHight - rightHeight ) >= 2 )//说明不平衡 
			{
				cout << "insert->" <<root->_kv.first << endl;//输出是那个节点出问题
				return false;
			}
			
			//顺便检查每一个节点的平衡因子
			if (rightHeight - leftHight != root->_bf)
			{
				cout << root->_kv.first << endl;//平衡因子出问题节点位置
				return false;
			}			

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

		int _Height(Node* root)
		{
			//求高度?
			//左树和右树的高度差

			if (root == nullptr)
			{
				return 0;
			}

		/*	int leftHeight = _Height(root->_left) + 1;
			int rightHeight = _Height(root->_right) + 1;
			return max(leftHeight, rightHeight);*/
			return max(_Height(root->_left), _Height(root->_right)) + 1;
		}



		int _size(Node* root)
		{
			return root == nullptr ? 0 : _size(root->_left) + _size(root->_right) + 1;
		}

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

			_Inorder(root->_left);
			cout << root->_kv.first << ":" << root->_kv.second << endl;
			_Inorder(root->_right);
		}

	private:
		Node* _root = nullptr;
	};

	void AVLTreetest()
	{

		int a[] = { 1,2,3};
		AVLTree<int, int> t;
		for (auto e : a)
		{
			t.Insert({ e,e });
		}


		t.Inorder();
		cout << t.size() << endl;
		cout << t.Height() << endl;
		
	}

	void AVLTreetest1()
	{

		int a[] = { 8, 3, 1, 10, 6, 4};
		AVLTree<int, int> t;
		for (auto e : a)
		{
			t.Insert({ e,e });
		}

		int b = 0;

		t.Inorder();
		cout << t.isBalance() << endl;

	}



	void AVLTreetest2()
	{

		int n = 10000000;//1000万个节点
		srand(time(0));
		vector<int> v;
		v.reserve(n);
		for (int i  =0 ;i<n;++i)
		{
			v.push_back(rand() + i);
		}

		size_t T1 = clock();
		AVLTree<int, int> t;
		for (auto e : v)
		{
			t.Insert(make_pair(e,e));
		}
		size_t T2 = clock();

		size_t T3 = clock();
		for (auto e : v)
		{
			t.find(e);
		}
		size_t T4 = clock();

		cout << "insert:" << T2 - T1 << endl;
		cout << "find:" << T4 - T3 << endl;
		

	}



}

  • 44
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

二十5画生

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值