【C++进阶】AVL树的实现

文章介绍了AVL树的概念,这是一种自平衡的二叉搜索树,确保左右子树高度差不超过1。在插入新节点后,通过更新平衡因子并根据情况进行四种类型的旋转(右单旋、左单旋、左右双旋、右左双旋)来保持树的平衡,从而保持高效的搜索性能。代码示例展示了插入节点和旋转处理的详细步骤。
摘要由CSDN通过智能技术生成

AVL树概念

二叉搜索树虽可以缩短查找的效率,但如果数据有序或接近有序二叉搜索树将退化为单支树,查找元素相当于在顺序表中搜索元素,效率低下。因此,两位俄罗斯的数学家
G.M.Adelson-Velskii和E.M.Landis在1962年
发明了一种解决上述问题的方法:当向二叉搜索树中插入新结点后,如果能保证每个结点的左右子树高度之差的绝对值不超过1(需要对树中的结点进行调整),即可降低树的高度,从而减少平均搜索长度。

AVL树性质

  • 它的左右子树都是AVL树
  • 左右子树高度之差(简称平衡因子)的绝对值不超过1(-1/0/1)

两种计算平衡因子的方法
法一:节点的左子树高度减右子树高度
在这里插入图片描述
法二:节点的右子树高度减左子树高度
在这里插入图片描述

如果一棵二叉搜索树是高度平衡的,它就是AVL树。如果它有n个结点,其高度可保持在
O ( l o g 2 n ) O(log_2 n) O(log2n),搜索时间复杂度O( l o g 2 n log_2 n log2n)。

AVL树节点的结构

实现的是KV模型
平衡因子的计算方法由第二种方法实现
在这里插入图片描述

AVL树的插入

分为三个步骤
1.按照二叉搜索树的方式插入新节点
2.更新整体节点的平衡因子
3.更新平衡因子的过程中判断是否需要旋转

插入新节点

bool Insert(const pair<K, V>& kv)
{
	Node* parent = nullptr;
	Node* cur = _root;

	//如果根为空
	if (_root == nullptr)
	{
		_root = new Node(kv);
		return true;
	}

	//寻找插入位置
	while (cur)
	{
				//如果要插入的值比cur的值小
		if (cur->_kv.first > kv.first)
		{
			//往左找
			parent = cur;
			cur = cur->_left;
		}
		//如果要插入的值比cur的值大
		else if (cur->_kv.first < kv.first)
		{
			//往右找
			parent = cur;
			cur = cur->_right;
		}
		//如果相等就不再插入
		else
		{
			return false;
		}
	}

	//插入
	Node* newnode = new Node(kv);
	cur = newnode;
	if (parent->_kv.first > kv.first)
	{
		parent->_left = newnode;
		newnode->_parent = parent;
		//父母节点更新平衡因子
		parent->_bf--;
	}
	else
	{
		parent->_right = newnode;
		newnode->_parent = parent;
		//父母节点更新平衡因子
		parent->_bf++;
	}
}

更新整体节点的平衡因子

更新整体节点的平衡因子过程中分三种情况

1.首先在插入的过程中,更新插入节点的父母节点的平衡因子
在这里插入图片描述


2.更新以后判断父母节点的平衡因子
1)更新后父母节点的平衡因子是0
停止更新,说明更新前父母节点的平衡因子是1或者-1,现在变成0也表明了父母节点的左右子树中高度矮的子树插入了新节点
在这里插入图片描述

if (parent->_bf == 0)
	{
		break;
	}

(2)更新后父母节点的平衡因子是1或者-1
继续向上更新, 说明更新前父母节点的平衡因子是0,现在是1或者-1,也表明父母节点的左右子树中的一颗树的高度变高了。父母节点所在的子树高度也变高了。
在这里插入图片描述
在这里插入图片描述

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

(3)更新后父母节点的平衡因子是2或者-2
说明父母节点所在的子树已经不平衡了需要旋转处理

在这里插入图片描述

else if (parent->_bf == 2 || parent->_bf == -2)
	{
					
		if (parent->_bf == -2 && cur->_bf == -1)
		{
			//右单旋
			RotateR(parent);
		}
		else if (parent->_bf == 2 && cur->_bf == 1)
		{
			//左单旋
			RotateL(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;
	}

整体的插入代码

//更新整体平衡因子
while (parent)
{
	if (parent->_bf == 0)
	{
		break;
	}
	else if (parent->_bf == 1 || parent->_bf == -1)
	{
		cur = parent;
		//继续向上更新
		parent = parent->_parent;
		if (parent)
		{
			if (parent->_left == cur)
			{
				parent->_bf--;
			}
			else
			{
				parent->_bf++;
			}
		}
					
	}
	else if (parent->_bf == 2 || parent->_bf == -2)
	{
					
		if (parent->_bf == -2 && cur->_bf == -1)
		{
			//右单旋
			RotateR(parent);
		}
		else if (parent->_bf == 2 && cur->_bf == 1)
		{
			//左单旋
			RotateL(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);
	}
}

旋转处理

旋转处理是为了处理保证二叉搜索树处于平衡状态,每当二叉搜索树不平衡时就会触发旋转。
旋转又分4种分别处理4种不平衡的情况。

右单旋

在这里插入图片描述
在这里插入图片描述

void RotateR(Node* parent)
{
	//要调整的节点
	Node* sub = parent;
	//要调整的节点的左孩子
	Node* subL = parent->_left;
	//要调整的节点的左孩子的右孩子
	Node* subLR = subL->_right;

	//要调整的节点的父母
	Node* subparent = sub->_parent;

	//重新链接关系
	if (subLR)
		subLR->_parent = sub;
	sub->_left = subLR;

	sub->_parent = subL;
	subL->_right = sub;

	subL->_parent = subparent;

	if (_root == sub)
	{
		_root = subL;
	}
	else
	{
		if (subparent->_left == sub)
		{
			subparent->_left = subL;
		}
		else
		{
			subparent->_right = subL;
		}
	}

	subL->_bf = sub->_bf = 0;
}

左单旋

在这里插入图片描述

void RotateL(Node* parent)
{
	//要调整的节点
	Node* sub = parent;
	//要调整的节点的右孩子
	Node* subR = parent->_right;
	//要调整的节点的有孩子的左孩子
	Node* subRL = subR->_left;

	//要调整的节点的父母
	Node* subparent = sub->_parent;

	//重新链接关系
	if (subRL)
		subRL->_parent = sub;
	sub->_right = subRL;

	sub->_parent = subR;
	subR->_left = sub;

	subR->_parent = subparent;

	if (_root == sub)
	{
		_root = subR;
	}
	else
	{
		if (subparent->_left == sub)
		{
			subparent->_left = subR;
		}
		else
		{
			subparent->_right = subR;
		}
	}
	subR->_bf = sub->_bf = 0;
}

左右双旋

在这里插入图片描述

void RotateLR(Node* parent)
{
	Node* subL = parent->_left;
	Node* subLR = subL->_right;
	int bf = subLR->_bf;
	RotateL(parent->_left);
	RotateR(parent);
	if (bf == -1)
	{
		parent->_bf = 1;
		subL->_bf = 0;
		subLR->_bf = 0;
	}
	else if (bf == 1)
	{
		parent->_bf = 0;
		subLR->_bf = 0;
		subL->_bf = -1;
	}
	else if (bf == 0)
	{
		parent->_bf = 0;
		subLR->_bf = 0;
		subL->_bf = 0;
	}
	else
	{
		assert(false);
	}
}

右左双旋

在这里插入图片描述

void RotateRL(Node* parent)
{
	Node* subR = parent->_right;
	Node* subRL = subR->_left;
	int bf = subRL->_bf;
	RotateR(parent->_right);
	RotateL(parent);
	if (bf == -1)
	{
		parent->_bf = 0;
		subR->_bf = 1;
		subRL->_bf = 0;
	}
	else if (bf == 1)
	{
		parent->_bf = -1;
		subRL->_bf = 0;
		subR->_bf = 0;
	}
	else if (bf == 0)
	{
		parent->_bf = 0;
		subRL->_bf = 0;
		subR->_bf = 0;
	}
	else
	{
		assert(false);
	}
			

}

AVL树的整体代码

AVL.h

#pragma once
#include<assert.h>
namespace lzf
{
	template<class K, class V>
	struct AVLTreeNode
	{
		AVLTreeNode<K, V>* _left;//该节点的左孩子
		AVLTreeNode<K, V>* _right;//该节点的右孩子
		AVLTreeNode<K, V>* _parent;//该节点的双亲节点
		pair<K, V> _kv;//该节点的kv

		int _bf;//该节点的平衡因子
		
		//构造函数
		AVLTreeNode(const pair<K, V>& kv)
			:_left(nullptr)
			,_right(nullptr)
			,_parent(nullptr)
			,_bf(0)
			,_kv(kv)
		{}
	};

	template<class K,class V>
	class AVLTree
	{
		typedef AVLTreeNode<K, V> Node;
	public:
		AVLTree()
			:_root(nullptr)
		{}
		bool Insert(const pair<K, V>& kv)
		{
			Node* parent = nullptr;
			Node* cur = _root;

			//如果根为空
			if (_root == nullptr)
			{
				_root = new Node(kv);
				return true;
			}

			//寻找插入位置
			while (cur)
			{
				//如果要插入的值比cur的值小
				if (cur->_kv.first > kv.first)
				{
					//往左找
					parent = cur;
					cur = cur->_left;
				}
				//如果要插入的值比cur的值大
				else if (cur->_kv.first < kv.first)
				{
					//往右找
					parent = cur;
					cur = cur->_right;
				}
				//如果相等就不再插入
				else
				{
					return false;
				}
			}

			//插入
			Node* newnode = new Node(kv);
			cur = newnode;
			if (parent->_kv.first > kv.first)
			{
				parent->_left = newnode;
				newnode->_parent = parent;
				//父母节点更新平衡因子
				parent->_bf--;
			}
			else
			{
				parent->_right = newnode;
				newnode->_parent = parent;
				//父母节点更新平衡因子
				parent->_bf++;
			}

			
			//更新整体平衡因子
			while (parent)
			{
				if (parent->_bf == 0)
				{
					break;
				}
				else if (parent->_bf == 1 || parent->_bf == -1)
				{
					cur = parent;
					//继续向上更新
					parent = parent->_parent;
					if (parent)
					{
						if (parent->_left == cur)
						{
							parent->_bf--;
						}
						else
						{
							parent->_bf++;
						}
					}
					
				}
				else if (parent->_bf == 2 || parent->_bf == -2)
				{
					
					if (parent->_bf == -2 && cur->_bf == -1)
					{
						//右单旋
						RotateR(parent);
					}
					else if (parent->_bf == 2 && cur->_bf == 1)
					{
						//左单旋
						RotateL(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);
		}
		bool IsbalanceTree()
		{
			return _IsbalanceTree(_root);
		}
	private:
		void RotateR(Node* parent)
		{
			//要调整的节点
			Node* sub = parent;
			//要调整的节点的左孩子
			Node* subL = parent->_left;
			//要调整的节点的左孩子的右孩子
			Node* subLR = subL->_right;

			//要调整的节点的父母
			Node* subparent = sub->_parent;

			//重新链接关系
			if (subLR)
				subLR->_parent = sub;
			sub->_left = subLR;

			sub->_parent = subL;
			subL->_right = sub;

			subL->_parent = subparent;

			if (_root == sub)
			{
				_root = subL;
			}
			else
			{
				if (subparent->_left == sub)
				{
					subparent->_left = subL;
				}
				else
				{
					subparent->_right = subL;
				}
			}

			subL->_bf = sub->_bf = 0;
		}
		void RotateL(Node* parent)
		{
			//要调整的节点
			Node* sub = parent;
			//要调整的节点的右孩子
			Node* subR = parent->_right;
			//要调整的节点的有孩子的左孩子
			Node* subRL = subR->_left;

			//要调整的节点的父母
			Node* subparent = sub->_parent;

			//重新链接关系
			if (subRL)
				subRL->_parent = sub;
			sub->_right = subRL;

			sub->_parent = subR;
			subR->_left = sub;

			subR->_parent = subparent;

			if (_root == sub)
			{
				_root = subR;
			}
			else
			{
				if (subparent->_left == sub)
				{
					subparent->_left = subR;
				}
				else
				{
					subparent->_right = subR;
				}
			}
			subR->_bf = sub->_bf = 0;
		}
		void RotateRL(Node* parent)
		{
			Node* subR = parent->_right;
			Node* subRL = subR->_left;
			int bf = subRL->_bf;
			RotateR(parent->_right);
			RotateL(parent);
			if (bf == -1)
			{
				parent->_bf = 0;
				subR->_bf = 1;
				subRL->_bf = 0;
			}
			else if (bf == 1)
			{
				parent->_bf = -1;
				subRL->_bf = 0;
				subR->_bf = 0;
			}
			else if (bf == 0)
			{
				parent->_bf = 0;
				subRL->_bf = 0;
				subR->_bf = 0;
			}
			else
			{
				assert(false);
			}
			

		}
		void RotateLR(Node* parent)
		{
			Node* subL = parent->_left;
			Node* subLR = subL->_right;
			int bf = subLR->_bf;
			RotateL(parent->_left);
			RotateR(parent);
			if (bf == -1)
			{
				parent->_bf = 1;
				subL->_bf = 0;
				subLR->_bf = 0;
			}
			else if (bf == 1)
			{
				parent->_bf = 0;
				subLR->_bf = 0;
				subL->_bf = -1;
			}
			else if (bf == 0)
			{
				parent->_bf = 0;
				subLR->_bf = 0;
				subL->_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);
		}

		int Hegiht(Node* root)
		{
			if (root == nullptr)
			{
				return 0;
			}

			return Hegiht(root->_left) > Hegiht(root->_right) ? Hegiht(root->_left) + 1 : Hegiht(root->_right) + 1;
		}
		bool _IsbalanceTree(Node* root)
		{
			if (root == nullptr)
			{
				return true;
			}

			int leftHeight = Hegiht(root->_left);
			int rightHeight = Hegiht(root->_right);

			if ((rightHeight - leftHeight) != root->_bf)
			{
				cout << root->_kv.first << "现在bf是:" << root->_bf <<" ";
				cout << "应该是:" << (rightHeight - leftHeight) << endl;
				return false;
			}

			return abs(rightHeight - leftHeight) < 2
				&& _IsbalanceTree(root->_left)
				&& _IsbalanceTree(root->_right);
		}
		
	private:
		Node* _root;
	};


	void Test_AVLTree1()
	{
		AVLTree<int, int> tree;
		tree.Insert(make_pair(5, 5));
		tree.Insert(make_pair(3, 3));
		tree.Insert(make_pair(8, 8));
		tree.Insert(make_pair(4, 4));
		tree.Insert(make_pair(6, 6));
		tree.Insert(make_pair(2, 2));
		tree.Insert(make_pair(7, 7));

		tree.InOrder();
		cout << tree.IsbalanceTree();

	}
	void Test_AVLTree2()
	{
		AVLTree<int, int> t;
		//int a[] = {5,4,3,2,1,0};
		//int a[] = { 16, 3, 7, 11, 9, 26, 18, 14, 15 };
		int a[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14 };
		for (auto e : a)
		{
			t.Insert(make_pair(e, e));
			cout << "Insert" << e << ":" << t.IsbalanceTree() << endl;
		}
		t.InOrder();
		cout << t.IsbalanceTree() << endl;
	}

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值