AVL树超详细图解及C++实现

AVL树,又名高度平衡搜索树,他会一直保证树的左右子树高度不超过1,一旦超过,就会进行调整,本篇博客采用平衡因子法,主要来讲如何用平衡因子控制平衡,以及AVL树的代码实现

首先介绍一下平衡因子,他存在于每个节点当中,当高度平衡时,平衡因子为0,左子树高1,那么平衡因子-1,右子树高1,平衡因子+1,例如图中这棵树

root的左子树比右子树高,所以平衡因子为-1,而left左右为空树,是平衡状态,那么平衡因子就为0。

介绍完了平衡因子调节规则,我们就来说一下AVL树的插入如何实现,首先还是按照搜索树的方式进行插入,新节点与树中节点比较key值,找到新节点该去的位置,创建新节点,并与父树连接,要提一点的是,相比于搜索树,AVL树节点中还要使用parents用来帮助AVL树的实现,连接之后,就要开始最复杂的一部分,即更新平衡因子,新节点无需更新,他一定是平衡的,所以要从新节点的parents进行更新,首先要进行判断,如果新节点为右,那么parents的平衡因子++,为左,平衡因子--,如果++或--后平衡因子为零,那么代表parents的左右树已经平衡,无需向上更新,如果为1,或者-1,那么代表parents父树的高度也已经改变,需要向上更新,即让parents = parents->parents,继续向上更新,同时再将一个变量将parents赋值之前的值进行记录,再进行比较,先看一下这部分的代码 

bool insert(const pair<key,value> kv)
		{
	        //找到新节点该插入的位置
			if (_root == nullptr)
			{
				_root = new Node(kv);
				return true;
			}
			Node* cur = _root;
			Node* parents = nullptr;
			while (cur)
			{
				if (cur->_kv.first > kv.first)
				{
					parents = cur;
					cur = cur->_left;
				}
				else if (cur->_kv.first < kv.first)
				{
					parents = cur;
					cur = cur->_right;
				}
				else
				{
					return false;
				}
			}
			//创建新节点并连接
			cur = new Node(kv);
			if (parents->_kv.first > cur->_kv.first)
			{
				parents->_left = cur;
				cur->_parents = parents;
			}
			else
			{
				parents->_right = cur;
				cur->_parents = parents;
			}

			//更新平衡因子
			while (parents)
			{
				if (parents->_right == cur)
				{
					parents->_bf++;
				}
				else if(parents->_left == cur)
				{
					parents->_bf--;
				}
				if (parents->_bf == 0)
				{
					break;
				}
				else if (parents->_bf == -1 || parents->_bf == 1)
				{
					cur = parents;
					parents = parents->_parents;
				}
					
			}
			return true;
		}

之后就到了AVL树的关键点,如果平衡因子等于2或-2,就代表需要进行旋转 ,我们先从一张图里看一下如何旋转的

这是一个左旋转 ,将cur的左给parents的右,parents成为cur的左,旋转完成后再更新cur和parents的parents指针指向,下面是左旋转代码

void Left_Rotate(Node* parents)
		{
			Node* subR = parents->_right;
			Node* subRL = subR->_left;
			subR->_left = parents;
			parents->_right = subRL;
			//旋转时如果subRL为空,则不需要parents
			if(subRL)
			    subRL->_parents = parents;
			Node* ppnode = parents->_parents;
			parents->_parents = subR;
			//parents->parents为空,说明parents为root
			if (_root == parents)
			{
				_root = subR;
				subR->_parents = nullptr;
			}
			else
			{
				if (ppnode->_left == parents)
					ppnode->_left = subR;
				else
					ppnode->_right = subR;
				subR->_parents = ppnode;
			}

			parents->_bf = subR->_bf = 0;

		}

下面我们想一下,什么情况下触发左旋转呢,从之前图中我们可以看到,parents平衡因子为2,cur为1,触发了左旋,同样,如果parents平衡因子为-2,cur为-1,那么会触发右旋,旋转逻辑与右转类似,将cur的右给parents的左,parents成为cur的右,下图是左旋图和右旋代码

void Right_Rotate(Node* parents)
		{
			Node* subL = parents->_left;
			Node* subLR = subL->_right;
			subL->_right = parents;
			parents->_left = subLR;
			if (subLR)
				subLR->_parents = parents;
			Node* ppnode = parents->_parents;
			parents->_parents = subL;

			if (_root == parents)
			{
				_root = subL;
				subL->_parents = nullptr;
			}
			else
			{
				if (ppnode->_left == parents)
					ppnode->_left = subL;
				else
					ppnode->_right = subL;

				subL->_parents = ppnode;
			}
			parents->_bf = subL->_bf = 0;
		}

可能有朋友看到这会很疑惑,AVL树貌似也很简单嘛,看不出什么难度,那么下面硬菜来了,如果parents为-2,而cur为1呢,如下图一样

那么这时我们好像进行左旋和右旋都无法解决,此时就需要进行两次旋转,先从cur开始旋转,将

newnode的左给cur的右,cur成为newnode的左,也就是先进行了一次左旋,旋转后如图

再对parents进行一次右旋,将newnode的右给parents的左,parents成为newnode的右,旋转后如图:

先左后右的旋转我们称为左右双旋,完整旋转图如下:

这是当parents为-2,cur为1的左右双旋,同样,当parents为2,cur为-1时,要进行右左双旋,我就不一一展示了,直接放完整旋转图,如下

可能有朋友还是觉得不难,不就是旋转了两次嘛,看起来也很简单,下面就要上AVL树的搬砖了,看各位能不能啃动。

其实逻辑上理解AVL树的旋转的确不算很难,最让初学者头疼的其实是旋转后AVL树的平衡因子调节,无论是左旋还是右旋,只需将旋转的两个节点的平衡因子置为零即可,因为左右旋转后他们两个都平衡了,重点在双旋的平衡因子调节 ,我们先来看左右双旋的平衡因子调节

此时是parents的平衡因子为-2,subL的平衡因子为1,subLR的平衡因子为-1,我们还是要先进行双旋,先将subL进行左旋转,subLR的左给subL的右,subL成为subLR的左,再对parents进行右旋转,旋转图如下 

可以看到,旋转完后,parents的平衡因子为1,subL和subLR皆为0

如果subLR的平衡因子一开始为1,下图为subLR的平衡因子为1的旋转过程

旋转完后,subL的平衡因子应该是-1,parents和subLR为0

左右双旋算上一开始讲解的那种情况,总共为三种可能,分别是subLR的平衡因子为1,-1,和0时的不同情况,下面是左右双旋的代码,可以配合图食用更佳

Node* subL = parents->_left;
			Node* subLR = subL->_right;
			int bf = 0;
			if (subLR)
				bf = subLR->_bf;
			Left_Rotate(parents->_left);
			Right_Rotate(parents);

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

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

右左双旋的逻辑与左右双旋类似,我们直接看suRL分别为1和-1时的两幅旋转图

从图中我们可以看到,当subLR为1时,旋转之后parents为-1,其余为0,档subLR为-1时,旋转之后subL为-1,那么右左双旋代码如下

void right_left_Rotate(Node* parents)
		{
			//先旋转
			Node* subR = parents->_right;
			Node* subRL = subR->_left;
			int bf = 0;
			if (subRL)
				bf = subRL->_bf;
			Right_Rotate(parents->_right);
			Left_Rotate(parents);

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

下面是insert的完整代码

bool insert(const pair<key,value> kv)
		{
	        //找到新节点该插入的位置
			if (_root == nullptr)
			{
				_root = new Node(kv);
				return true;
			}
			Node* cur = _root;
			Node* parents = nullptr;
			while (cur)
			{
				if (cur->_kv.first > kv.first)
				{
					parents = cur;
					cur = cur->_left;
				}
				else if (cur->_kv.first < kv.first)
				{
					parents = cur;
					cur = cur->_right;
				}
				else
				{
					return false;
				}
			}
			//创建新节点并连接
			cur = new Node(kv);
			if (parents->_kv.first > cur->_kv.first)
			{
				parents->_left = cur;
				cur->_parents = parents;
			}
			else
			{
				parents->_right = cur;
				cur->_parents = parents;
			}

			//更新平衡因子
			while (parents)
			{
				if (parents->_right == cur)
				{
					parents->_bf++;
				}
				else if(parents->_left == cur)
				{
					parents->_bf--;
				}
				if (parents->_bf == 0)
				{
				/*	cur = parents;
					parents = parents->_parents;*/
					break;
				}
				else if (parents->_bf == -1 || parents->_bf == 1)
				{
					cur = parents;
					parents = parents->_parents;
				}

				//走到这说明树的平衡性打破,需要旋转
				else if (parents->_bf == 2 || parents->_bf== -2)
				{
				      if (parents->_bf == 2)
				      {

					      if (cur->_bf == 1)
					      {
						    Left_Rotate(parents);
					      }
					      else if (cur->_bf == -1)
					      {
						    right_left_Rotate(parents);
					      }
				      }
				      else if (parents->_bf == -2)
				      {
					      if (cur->_bf == 1)
					      {
						    left_right_Rotate(parents);
					      }
				     	  else if (cur->_bf == -1)
					      {
						     Right_Rotate(parents);
					       }
				      }
				       break;
				}
				
			}
			return true;
		}

insert里相应要进行旋转的地方在前面图中我们已经可以清晰的看出来了,最后是我对AVL测试的所有代码

#pragma once
#include<utility>
#include<iostream>
#include<assert.h>
using std::make_pair;
using std::pair;
using std::cout;
using std::endl;
namespace sxh_AVLTree
{
	template<typename key,typename value>
	struct _AVLTreeNode
	{
		_AVLTreeNode<key,value>* _left;
		_AVLTreeNode<key, value>* _right;
		_AVLTreeNode<key, value>* _parents;
		int _bf;

		pair<key, value> _kv;
		_AVLTreeNode(const pair<key,value> kv):_kv(kv),_bf(0),_left(nullptr),_right(nullptr),_parents(nullptr){}
	};
	template<typename key,typename value>
	class AVLTree
	{
		typedef _AVLTreeNode<key,value> Node;
	public:
		Node* _root;
	public:
		AVLTree(const pair<key, value> kv) :_root(new Node(kv)) {}
		bool insert(const pair<key,value> kv)
		{
	        //找到新节点该插入的位置
			if (_root == nullptr)
			{
				_root = new Node(kv);
				return true;
			}
			Node* cur = _root;
			Node* parents = nullptr;
			while (cur)
			{
				if (cur->_kv.first > kv.first)
				{
					parents = cur;
					cur = cur->_left;
				}
				else if (cur->_kv.first < kv.first)
				{
					parents = cur;
					cur = cur->_right;
				}
				else
				{
					return false;
				}
			}
			//创建新节点并连接
			cur = new Node(kv);
			if (parents->_kv.first > cur->_kv.first)
			{
				parents->_left = cur;
				cur->_parents = parents;
			}
			else
			{
				parents->_right = cur;
				cur->_parents = parents;
			}

			//更新平衡因子
			while (parents)
			{
				if (parents->_right == cur)
				{
					parents->_bf++;
				}
				else if(parents->_left == cur)
				{
					parents->_bf--;
				}
				if (parents->_bf == 0)
				{
				/*	cur = parents;
					parents = parents->_parents;*/
					break;
				}
				else if (parents->_bf == -1 || parents->_bf == 1)
				{
					cur = parents;
					parents = parents->_parents;
				}

				//走到这说明树的平衡性打破,需要旋转
				else if (parents->_bf == 2 || parents->_bf== -2)
				{
				      if (parents->_bf == 2)
				      {

					      if (cur->_bf == 1)
					      {
						    Left_Rotate(parents);
					      }
					      else if (cur->_bf == -1)
					      {
						    right_left_Rotate(parents);
					      }
				      }
				      else if (parents->_bf == -2)
				      {
					      if (cur->_bf == 1)
					      {
						    left_right_Rotate(parents);
					      }
				     	  else if (cur->_bf == -1)
					      {
						     Right_Rotate(parents);
					       }
				      }
				       break;
				}
				
			}
			return true;
		}

		void Left_Rotate(Node* parents)
		{
			Node* subR = parents->_right;
			Node* subRL = subR->_left;
			subR->_left = parents;
			parents->_right = subRL;
			//旋转时如果subRL为空,则不需要parents
			if(subRL)
			    subRL->_parents = parents;
			Node* ppnode = parents->_parents;
			parents->_parents = subR;
			//parents->parents为空,说明parents为root
			if (_root == parents)
			{
				_root = subR;
				subR->_parents = nullptr;
			}
			else
			{
				if (ppnode->_left == parents)
					ppnode->_left = subR;
				else
					ppnode->_right = subR;
				subR->_parents = ppnode;
			}

			parents->_bf = subR->_bf = 0;

		}

		void Right_Rotate(Node* parents)
		{
			Node* subL = parents->_left;
			Node* subLR = subL->_right;
			subL->_right = parents;
			parents->_left = subLR;
			if (subLR)
				subLR->_parents = parents;
			Node* ppnode = parents->_parents;
			parents->_parents = subL;

			if (_root == parents)
			{
				_root = subL;
				subL->_parents = nullptr;
			}
			else
			{
				if (ppnode->_left == parents)
					ppnode->_left = subL;
				else
					ppnode->_right = subL;

				subL->_parents = ppnode;
			}
			parents->_bf = subL->_bf = 0;
		}
		void right_left_Rotate(Node* parents)
		{
			//先旋转
			Node* subR = parents->_right;
			Node* subRL = subR->_left;
			int bf = 0;
			if (subRL)
				bf = subRL->_bf;
			Right_Rotate(parents->_right);
			Left_Rotate(parents);

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

		void left_right_Rotate(Node* parents)
		{
			Node* subL = parents->_left;
			Node* subLR = subL->_right;
			int bf = 0;
			if (subLR)
				bf = subLR->_bf;
			Left_Rotate(parents->_left);
			Right_Rotate(parents);

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

			}
			else if(bf == 0)
			{
				parents->_bf = 0;
				subL->_bf = 0;
			}
			else
				assert(false);
		}
		void __InOrder(Node* root)
		{
			if (root == nullptr)
			{
				return;
			}
			__InOrder(root->_left);
			std::cout << root->_kv.first<< ":" << root->_kv.second << endl;
			__InOrder(root->_right);
		}

		void InOrder()
		{
			__InOrder(this->_root);
			std::cout << std::endl;
		}

		int Height(Node* root)
		{
			if (root == nullptr)
				return 0;
			
			int LeftHeight = Height(root->_left);
			int RightHeight = Height(root->_right);

			return LeftHeight > RightHeight ? LeftHeight+1 : RightHeight+1;
		}

		bool _Isbalance(Node* root)
		{
			if (root == nullptr)
				return true;

			int LeftHeight = Height(root->_left);
			int RightHeight = Height(root->_right);

			if (RightHeight - LeftHeight!= root->_bf)
			{
				cout << "平衡因子异常" << root->_bf << endl;
				cout << "rightheight - leftheight = " << RightHeight - LeftHeight << endl;
				return false;
			}

			return abs(RightHeight - LeftHeight) < 2
				&& _Isbalance(root->_left)
				&& _Isbalance(root->_right);
		}

		bool Isbalance()
		{
			return _Isbalance(this->_root);
		}
	};

}

有人看的话我会来补一下删除的逻辑讲解,没人看就不写了,写这篇博客已经一个多小时了,find依旧是搜索树的find,这个相信要学AVL树的各位都会写

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值