AVL树(高度平衡二叉搜索树)基本原理及简单实现

目录

一.AVL树的性质

二.AVL树的节点定义

三.AVL树的插入

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

2.调整节点的平衡因子

四.AVL树的旋转处理

1.新节点插入到较高右子树的右边---右右,进行左单旋

 2.新节点插入到较高左子树的左侧---左左,进行右单旋

 3.新节点插入到较高左子树的右侧--左右,先进行左单旋,再进行右单旋

4.新节点插入到较高右子树的左侧---右左,先进行右单旋,再进行左单旋

五.AVL树简单实现的整体代码


一.AVL树的性质

1.它的左右子树都是AVL树

2.左右子树高度之差(简称平衡因子)的绝对值不超过1(-1/0/1)

如果一颗二叉搜索树是高度平衡的,它就是AVL树。假设它的节点数为n,其高度可保持在\log_2 n,搜索时间复杂度为O(\log_2 n)。

二.AVL树的节点定义

template<class K, class V>
struct AVLTreeNode
{
	AVLTreeNode<K, V>* _left;
	AVLTreeNode<K, V>* _right;
	AVLTreeNode<K, V>* _parent;

	pair<K, V> _kv;
	int _bf;   //平衡因子balance factor

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

};

三.AVL树的插入

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

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

2.调整节点的平衡因子

而在这两点中,更新平衡因子尤为重要,其详细过程课分为以下几点

①.当新增节点在右时,parent->_bf需要+1,新增节点在左时,parent->_bf需要-1。

②.更新后,parent->_bf == 1 or -1,说明parent插入前的平衡因子为0,说明原左右子树高度相等,插入后有一边子树变高,此时改变了parent的高度,便需要往上继续更新平衡因子。

③.更新后,parent->_bf == 0,说明parent插入前的平衡因子为 1 or -1,说明左右子树一边高一边低,插入后两边变为一样高度,parent所在子树高度不变,不需要继续向上面的节点更新平衡因子。

④.更新后,parent->_bf == 2 or -2,说明parent插入前的平衡因子为 1 or -1,此时原左右子树已经存在一边高一边低的情况,继续插入的节点导致parent的平衡因子变为 2 or -2,平衡被打破,则parent所在子树需要旋转处理

⑤.更新后,parent->_bf > 2 or parent->_bf < -2的值,存在这种情况,说明在插入前的就已经不是AVL树,需要检查之前的树。

下图将简略描述以上几点:

对于第四点旋转处理将在下方详细介绍。

四.AVL树的旋转处理

AVL树的旋转处理又分为四种。

1.新节点插入到较高右子树的右边---右右,进行左单旋

 

通过上图发现当对较高右子树的右边进行插入时,向上更新平衡因子,造成parent节点平衡因子变为2,此时需要进行旋转处理,来降低右子树的高度来达到新的平衡,可以看出将60节点变为新的parent节点,再将b子树链接到30节点的右边,便可以达到新的平衡。

具体左旋代码如下:

void RotateL(Node* parent)
	{	
		//记父节点的右为subR
		//subR的左为subRL
		Node* subR = parent->_right;
		Node* subRL = subR->_left;
		//将父节点与subRL进行链接
		parent->_right = subRL;
		if (subRL)
			subRL->_parent = parent;
		//记parent节点的上一级节点为ppNode(方便更新平衡因子)
		Node* ppNode = parent->_parent;
		//继续链接
		subR->_left = parent;
		parent->_parent = subR;
		//原parent节点为根节点的情况
		if (_root == subR)
		{
			_root = subR;
			subR->_parent = nullptr;
		}
		else  //原parent节点为一棵子树
		{	
			//将subR与原parent的上层节点进行链接
			//链接到左
			if (ppNode->_left == parent)
			{
				ppNode->_left = subR;
			}
			else //链接到右
			{
				ppNode->_right = subR;
			}
			subR->_parent = ppNode;
		}
		subR->_bf = parent->_bf = 0;
	}

 2.新节点插入到较高左子树的左侧---左左,进行右单旋

 通过上图发现当对较高左子树的左边进行插入时,向上更新平衡因子,造成parent节点平衡因子变为-2,此时需要进行旋转处理,来降低左子树的高度来达到新的平衡,可以看出将30节点变为新的parent节点,再将b子树链接到60节点的左边,便可以达到新的平衡。

具体左旋代码如下:

void RotateR(Node* parent)
	{	
		//记父节点的左为subL
		//subL的右为subLR
		Node* subL = parent->_left;
		Node* subLR = subL->_right;
		//将subLR与父节点的进行链接
		parent->_left = subLR;
		if (subLR)
		{
			subLR->_parent = parent;
		}
		//记parent节点的上一级节点为ppNode(方便更新平衡因子)
		Node* ppNode = parent->_parent;
		//继续链接
		subL->_right = parent;
		parent->_parent = subL;
		//原parent节点为根节点的情况
		if (_root == parent)
		{	
			_root = subL;
			subL->_parent = nullptr;
		}
		else //原parent节点为一棵子树
		{	
			//将subL与原parent的上层节点进行链接
			//链接到左
			if (ppNode->_left == parent)
			{
				ppNode->_left = subL;
			}
			else //连接到右
			{
				ppNode->_right = subL;
			}
		}
		//更新平衡因子
		subL->_bf = parent->_bf = 0; 
	}

 3.新节点插入到较高左子树的右侧--左右,先进行左单旋,再进行右单旋

 

而左右双旋又可以分为三种情况(便于调整平衡因子),以上图为例。

1.b插入新增节点,引发双旋。(图中情况)

2.c插入新增节点,引发双选。

3.a/b/c/d为空树,60即为新增节点,引发双旋。

双旋的过程与单旋并没有什么区别,因此可以复用单旋的代码,需要注意的是对平衡因子的调整。

具体左右双旋代码如下:

void RotateLR(Node* parent)
	{	
		//记父节点的左为subL
		//subL的右为subLR
		Node* subL = parent->_left;
		Node* subLR = subL->_right;
		//记录subLR的平衡因子,方便针对插入节点的情况,进行平衡因子的调整
		int bf = subLR->_bf;
		//左单旋
		RotateL(parent->_left);
		//右单旋
		RotateLR(parent);

		//双旋过后subLR平衡因子为0
		subLR->_bf = 0;
		//对父节点和subL平衡因子进行调整的三种情况
		//新增节点在subLR的右
		if (bf == 1)
		{	
			parent->_bf = 0;
			subL->_bf = -1;
		}
		//新增节点在subLR的左
		else if (bf == -1)
		{
			parent->_bf = 1;
			subL->_bf = 0;
		}
		//该节点即为新增节点
		else if (bf == 0)
		{
			parent->_bf = 0;
			subL->_bf = 0;
		}
		else
		{
			assert(false);
		}
	}

4.新节点插入到较高右子树的左侧---右左,先进行右单旋,再进行左单旋

 

同样右左双旋也可以分为三种情况(便于调整平衡因子),以上图为例。

1.b插入新增节点,引发双旋。

2.c插入新增节点,引发双选。(图中情况)

3.a/b/c/d为空树,60即为新增节点,引发双旋。

双旋的过程与单旋并没有什么区别,因此可以复用单旋的代码,需要注意的是对平衡因子的调整。

具体右左双旋的代码如下:

void RotateRL(Node* parent)
	{	
		//记父节点的右为subR
		//subR的左为subRL
		Node* subR = parent->_right;
		Node* subRL = subR->_left;
		//记录subRL的平衡因子,方便针对插入节点的情况,进行平衡因子的调整
		int bf = subRL->_bf;
		//右单旋
		RotateR(parent->_right);
		//左单旋
		RotateL(parent);
		
		//双旋后subRL的平衡因子为0
		subRL->_bf = 0;
		//对父节点和subR平衡因子进行调整的三种情况
		//新增节点在subRL的右
		if (bf == 1)
		{
			subR->_bf = 0;
			parent->_bf = -1;
		}
		//新增节点在subRL的左
		else if (bf == -1)
		{
			subR->_bf = 1;
			parent->_bf = 0;
		}
		//该节点即为新增节点
		else if (bf == 0)
		{
			subR->_bf = 0;
			parent->_bf = 0;
		}
		else
		{
			assert(false);
		}
	}

五.AVL树简单实现的整体代码

#pragma once
#include<iostream>
#include <map>
#include <string>
using namespace std;
template<class K, class V>
struct AVLTreeNode
{
	AVLTreeNode<K, V>* _left;
	AVLTreeNode<K, V>* _right;
	AVLTreeNode<K, V>* _parent;

	pair<K, V> _kv;
	int _bf;   //平衡因子balance factor

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

};

template<class K, class V>
struct AVLTree
{
	typedef AVLTreeNode<K, V> Node;
public:
	bool Insert(const pair<K, V>& kv)
	{	
		//原树无节点,直接在根插入
		if (_root == nullptr)
		{
			_root = new Node(kv);
			return true;
		}
		
		//原树有节点,设根为当前节点,父节点为空
		Node* parent = nullptr;
		Node* cur = _root;
		while (cur)
		{	
			//插入时当前值小于新插入的值
			//将新节点插入到当前节点的右边
			//改变父节点位置
			if (cur->_kv.first < kv.first)
			{
				parent = cur;
				cur = cur->_right;
			}
			//插入时当前值大于新插入的值
			//将新节点插入到当前节点的左边
			//改变父节点位置
			else if (cur->_kv.first > kv.first)
			{
				parent = cur;
				cur = cur->_left;
			}
			else
			{
				return false;
			}
		}
			//在子树插入
			cur = new Node(kv);
			if (parent->_kv.first < kv.first)
			{
				parent->_right = cur;
			}
			else // (parent->_kv.first > kv.first)
			{
				parent->_left = cur;
			}

			cur->_parent = parent;

			//控制平衡
			//更新平衡因子
			while (parent)
			{
				if (cur = parent->_right)
				{
					parent->_bf++
				}
				else //(cur = parent->left)
				{
					parent->_bf--;
				}

				if (parent->_bf == 0)
				{
					break;
				}
				else if (abs(parent->_bf) == 1)   //向上调整
				{
					parent = parent->_parent;
					cur = cur->_parent;
				}
				else if (abs(parent->_bf) == 2) //说明此时parent所在子树已不平衡,需要进行旋转平衡
				{
					if (parent->_bf == 2 && cur->_bf == 1)
					{
						RotateL(parent);
					}
					else 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)
					{
						RotateRL(parent);
					}
					else
					{
						assert(false);
					}
					break;
				}
				else
				{
					assert(false);
				}
			}
		return true;
	}

	void InOrder()
	{
		_InOrder(_root);
		cout << endl;
	}

	bool IsBalance()
	{
		return _IsBalance(_root);
	}
private:
	//计算高度
	int Height(Node* root)
	{
		if (root == nullptr)
		{
			return 0;
		}
	//递归累加
		return max(Height(root->_left), Height(root->_right)) + 1;
	}
	//判断是否平衡
	bool _IsBalance(Node* root)
	{	
		//空树平衡
		if (root == nullptr)
		{
			return true;
		}
		//递归找左子树高度
		int leftHt = Height(root->_left);
		//递归找右子树高度
		int rightHt = Height(root->_right);
		//查看平衡因子是否在(-1/0/1)的范围
		int diff = rightHt - leftHt;
		if (diff != root->_bf)
		{
			cout << root->_kv.first << "平衡因子异常" << endl;
			return false;
		}
		//递归找左子树是否异常和递归找右子树是否异常
		return abs(diff) < 2
			&& _IsBalance(root->_left)
			&& _IsBalance(root->_right)
	}


	void RotateL(Node* parent)
	{	
		//记父节点的右为subR
		//subR的左为subRL
		Node* subR = parent->_right;
		Node* subRL = subR->_left;
		//将父节点与subRL进行链接
		parent->_right = subRL;
		if (subRL)
			subRL->_parent = parent;
		//记parent节点的上一级节点为ppNode(方便更新平衡因子)
		Node* ppNode = parent->_parent;
		//继续链接
		subR->_left = parent;
		parent->_parent = subR;
		//原parent节点为根节点的情况
		if (_root == subR)
		{
			_root = subR;
			subR->_parent = nullptr;
		}
		else  //原parent节点为一棵子树
		{	
			//将subR与原parent的上层节点进行链接
			//链接到左
			if (ppNode->_left == parent)
			{
				ppNode->_left = subR;
			}
			else //链接到右
			{
				ppNode->_right = subR;
			}
			subR->_parent = ppNode;
		}
		subR->_bf = parent->_bf = 0;
	}

	void RotateR(Node* parent)
	{	
		//记父节点的左为subL
		//subL的右为subLR
		Node* subL = parent->_left;
		Node* subLR = subL->_right;
		//将subLR与父节点的进行链接
		parent->_left = subLR;
		if (subLR)
		{
			subLR->_parent = parent;
		}
		//记parent节点的上一级节点为ppNode(方便更新平衡因子)
		Node* ppNode = parent->_parent;
		//继续链接
		subL->_right = parent;
		parent->_parent = subL;
		//原parent节点为根节点的情况
		if (_root == parent)
		{	
			_root = subL;
			subL->_parent = nullptr;
		}
		else //原parent节点为一棵子树
		{	
			//将subL与原parent的上层节点进行链接
			//链接到左
			if (ppNode->_left == parent)
			{
				ppNode->_left = subL;
			}
			else //连接到右
			{
				ppNode->_right = subL;
			}
		}
		//更新平衡因子
		subL->_bf = parent->_bf = 0; 
	}

	void RotateLR(Node* parent)
	{	
		//记父节点的左为subL
		//subL的右为subLR
		Node* subL = parent->_left;
		Node* subLR = subL->_right;
		//记录subLR的平衡因子,方便针对插入节点的情况,进行平衡因子的调整
		int bf = subLR->_bf;
		//左单旋
		RotateL(parent->_left);
		//右单旋
		RotateLR(parent);

		//双旋过后subLR平衡因子为0
		subLR->_bf = 0;
		//对父节点和subL平衡因子进行调整的三种情况
		//新增节点在subLR的右
		if (bf == 1)
		{	
			parent->_bf = 0;
			subL->_bf = -1;
		}
		//新增节点在subLR的左
		else if (bf == -1)
		{
			parent->_bf = 1;
			subL->_bf = 0;
		}
		//该节点即为新增节点
		else if (bf == 0)
		{
			parent->_bf = 0;
			subL->_bf = 0;
		}
		else
		{
			assert(false);
		}
	}

	void RotateRL(Node* parent)
	{	
		//记父节点的右为subR
		//subR的左为subRL
		Node* subR = parent->_right;
		Node* subRL = subR->_left;
		//记录subRL的平衡因子,方便针对插入节点的情况,进行平衡因子的调整
		int bf = subRL->_bf;
		//右单旋
		RotateR(parent->_right);
		//左单旋
		RotateL(parent);
		
		//双旋后subRL的平衡因子为0
		subRL->_bf = 0;
		//对父节点和subR平衡因子进行调整的三种情况
		//新增节点在subRL的右
		if (bf == 1)
		{
			subR->_bf = 0;
			parent->_bf = -1;
		}
		//新增节点在subRL的左
		else if (bf == -1)
		{
			subR->_bf = 1;
			parent->_bf = 0;
		}
		//该节点即为新增节点
		else if (bf == 0)
		{
			subR->_bf = 0;
			parent->_bf = 0;
		}
		else
		{
			assert(false);
		}
	}

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值