C++_进阶:AVL树性质及模拟实现


在这里插入图片描述

1. 💮AVL树的概念

二叉搜索树虽可以缩短查找的效率,但如果数据有序或接近有序二叉搜索树将退化为单支树,查找元素相当于在顺序表中搜索元素,效率低下

🔅因此,两位俄罗斯的数学家G.M.Adelson-Velskii和E.M.Landis在1962年发明了一种解决上述问题的方法:📋当向二叉搜索树中插入新结点后,如果能保证每个结点的左右子树高度之差的绝对值不超过1(需要对树中的结点进行调整),即可降低树的高度,从而减少平均搜索长度,这样的树就是AVL树

📋一棵AVL树是具有以下性质的二叉搜索树

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

在这里插入图片描述

🔅如果一棵二叉搜索树是高度平衡(满足以上两个条件)的,它就是AVL树。如果它有n个结点,其高度可保持在 O(logn) , 搜索时间复杂度O(logn)

2. 💮AVL树节点的定义

AVL的节点在原来二叉搜索树的节点上,增加了两个成员:父节点指针变量,以及平衡因子变量

template<class K, class V>
struct AVLTreeNode
{
	pair<K, V> _kv;
	AVLTreeNode<K, V>* _left;
	AVLTreeNode<K, V>* _right;
	AVLTreeNode<K, V>* _parent;
	int _bf; // balance factor

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

3. 💮AVL树的插入

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

  • 按照二叉搜索树的方式插入新节点
  • 调整节点的平衡因子

🔅更新平衡因子规则:
1.📝 插入的是左子树的节点,平衡因子- -,插入的是右子树的节点,平衡因子++
在这里插入图片描述
2. 📝调节后的平衡因子为1/-1,0,2/-2 往下需要做的是不同的。调节后的平衡因子为0时,停止向上更新平衡因子

在这里插入图片描述
调节后的平衡因子为1/-1时,继续向上调整平衡因子

在这里插入图片描述
调节后的平衡因子为2/-2时,就要调整树了,并且不再向上调整平衡因子
在这里插入图片描述
在这里插入图片描述
代码示例:

//接收一个 键值对pair类型 作为插入的值
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->_left = cur;
	}
	cur->_parent = parent;

	
	// 更新平衡因子
	while (parent)
	{	
		// 更新双亲的平衡因子
		//先判断插入的位置是父亲的左边还是右边
		//左边-- 右边++
		if (cur == parent->_left)
			parent->_bf--;
		else
			parent->_bf++;
			
		// 更新后检测双亲的平衡因子
		//如果平衡因子为0,停止向上更新平衡因子
		if (parent->_bf == 0)
		{
			break;
		}
		else if (parent->_bf == 1 || parent->_bf == -1)
		{
			//调节后的平衡因子为1/-1时,继续向上调整平衡因子
			// 插入前双亲的平衡因子是0,插入后双亲的平衡因为为1 或者 -1 ,说明以双亲为根的二叉树
			// 的高度增加了一层,因此需要继续向上调整
			cur = parent;
			parent = parent->_parent;
		}
		else if (parent->_bf == 2 || parent->_bf == -2)
		{
			// 调节后的平衡因子为2/-2时,就要调整树了,并且不再向上调整平衡因子
			// 双亲的平衡因子为2/-2,违反了AVL树的平衡性,需要对以pParent
			// 为根的树进行旋转处理
			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)
			{
				RotateRL(parent);
			}
			else 
			{
				RotateLR(parent);
			}
			break;
		}
		else
		{
			assert(false);
		}
	}

	return true;
}

4. 💮AVL树的旋转

🔅AVL树之所以能保持平衡,原因在于AVL树会 旋转,上面Insert代码中如果平衡因子== 2/-2时,RotateL,RotateR,RotateRL,RotateLR就是调整该树的函数。

🔅如果在一棵原本是平衡的AVL树中插入一个新节点,可能造成不平衡,此时必须调整树的结构,使之平衡化根据节点插入位置的不同,AVL树的旋转分为四种

4.1 🚩右单旋

1️⃣ 📝新节点插入较高左子树的左侧—左左:右单旋在这里插入图片描述
🔅最终根据情况重新设定平衡因子:cur的平衡因子变为0,parent的平衡因子变为0。

void  RotateR(Node* parent)
{
	// subL: parent的左孩子
	// subLR: Parent左孩子的右孩子
	Node* subL = parent->_left;
	Node* subLR = subL->_right;

	// 旋转完成之后,10的右孩子作为父亲的左孩子
	parent->_left = subLR;
	// 如果10的左孩子的右孩子存在,更新亲双亲
	if (subLR)
		subLR->_parent = parent;
	
	// 因为30可能是棵子树,因此在更新其双亲前必须先保存30的双亲
	Node* parentParent = parent->_parent;
	
	// 30作为 10的右孩子
	subL->_right = parent;
	// 更新30的双亲
	parent->_parent = subL;
	
	// 如果30是根节点,根新指向根节点的指针
	if (parentParent == nullptr)
	{
		_root = subL;
		subL->_parent = nullptr;
	}
	else
	{	// 如果30是子树,可能是其双亲的左子树,也可能是右子树
		if (parent == parentParent->_left)
		{
			parentParent->_left = subL;
		}
		else
		{
			parentParent->_right = subL;
		}

		subL->_parent = parentParent;
	}
	// 根据调整后的结构更新部分节点的平衡因子
	parent->_bf = subL->_bf = 0;
}

4.2 🚩左单旋

2️⃣ 📝新节点插入较高右子树的右侧—右右:左单旋
在这里插入图片描述
🔅最终根据情况重新设定平衡因子:cur的平衡因子变为0,parent的平衡因子变为0。

//与右旋逻辑大体一致
void RotateL(Node* parent)
{
	Node* subR = parent->_right;
	Node* subRL = subR->_left;

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

	Node* parentParent = parent->_parent;

	subR->_left = parent;
	parent->_parent = subR;

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

		subR->_parent = parentParent;
	}

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

4.3 🚩左右双旋

3.📝 新节点插入较高左子树的右侧—左右:先左单旋再右单旋
在这里插入图片描述

在这里插入图片描述
🔅但是实际情况中,插入的节点在son节点的左边还是右边我们是不知道的,也就是说,插入后的b树与c树的高度其实不确定的,只能确定不是h就是h-1,这会影响最终平衡因子的分布。根据bc树高度的不同可以分出三种情况:
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

//左右双旋逻辑:
void RotateLR(Node* parent)
{
	//parent的左节点 -> cur
	Node* subL = parent->_left;
	//parent的左节点的右节点 -> son
	Node* subLR = subL->_right;
	// 记下 son 的平衡因子
	// 用son的平衡因子判断bc树不同的高度情况
	int bf = subLR->_bf;
	//复用左旋逻辑 , 对cur左旋
	RotateL(parent->_left);
	//复用右旋逻辑 , 对parent右旋
	RotateR(parent);
	
	//当b = h,c = h
	if (bf == 0)
	{
		subL->_bf = 0;
		subLR->_bf = 0;
		parent->_bf = 0;
	}//当b = h-1,c = h
	else if (bf == 1)
	{
		subL->_bf = -1;
		subLR->_bf = 0;
		parent->_bf =0;
	}//b = h,c = h-1
	else if (bf == -1)
	{
		subL->_bf = 0;
		subLR->_bf = 0;
		parent->_bf = 1;
	}
	else
	{
		assert(false);
	}
}

4.4 🚩右左双旋

4️⃣新节点插入较高右子树的左侧—右左:先右单旋再左单旋
在这里插入图片描述

在这里插入图片描述

//右左双旋逻辑:与左右双旋逻辑大体一致
void RotateRL(Node* parent)
{
	Node* subR = parent->_right;
	Node* subRL = subR->_left;
	int bf = subRL->_bf;
	RotateR(parent->_right);
	RotateL(parent);

	if (bf == 0)
	{
		subR->_bf = 0;
		subRL->_bf = 0;
		parent->_bf = 0;
	}
	else if (bf == 1)
	{
		subR->_bf = 0;
		subRL->_bf =0;
		parent->_bf = -1;
	}
	else if (bf == -1)
	{
		subR->_bf = 1;
		subRL->_bf = 0;
		parent->_bf = 0;
	}
	else
	{
		assert(false);
	}


}

5. 💮AVL树的验证

🔅AVL树是在二叉搜索树的基础上加入了平衡性的限制,因此要验证AVL树,可以分两步

  1. 验证其为二叉搜索树。如果中序遍历可得到一个有序的序列,就说明为二叉搜索树。
  2. 验证其为平衡树每个节点子树高度差的绝对值不超过1(注意节点中如果没有平衡因子)节点的平衡因子是否计算正确。
bool _IsAVLTree(Node* pRoot)
{
	if (pRoot == nullptr)
	{
		return true;
	}
	if (_Height(pRoot->_right) - _Height(pRoot->_left) != pRoot->_bf || abs(_Height(pRoot->_right) - _Height(pRoot->_left)>1))
	{
		cout << pRoot->_kv.first << ":" << pRoot->_kv.second << " bf=" << pRoot->_bf << endl;
		cout << "_Height(pRoot->_right) - _Height(pRoot->_left)=" << _Height(pRoot->_right) - _Height(pRoot->_left);
		return false;
	}
	return _IsAVLTree(pRoot->_left) && _IsAVLTree(pRoot->_right);
}

//计算树高度函数
int _Height(Node* pRoot)
{
	if (pRoot == nullptr)
	{
		return 0;
	}

	size_t leftH = _Height(pRoot->_left);
	size_t rightH = _Height(pRoot->_right);
	return leftH > rightH ? leftH + 1 : rightH + 1;
}

6. 💮AVL树模拟实现

#pragma once
#include<iostream>
#include <assert.h>
using namespace std;

//AVL采用键值对数结构
template<class K, class V>
struct AVLTreeNode
{
	pair<K, V> _kv;
	AVLTreeNode<K, V>* _left;
	AVLTreeNode<K, V>* _right;
	AVLTreeNode<K, V>* _parent;
	int _bf; // balance factor

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

template<class K, class V>
class AVLTree
{
	typedef AVLTreeNode<K, V> Node;
public:
	//默认生成默认构造函数
	AVLTree() = default;
	
	//拷贝构造
	AVLTree(const AVLTree<K, V>& t)
	{
		_root = Copy(t._root);
	}
	//赋值重载
	AVLTree<K, V>& operator=(AVLTree<K, V> t)
	{
		swap(_root, t._root);
		return *this;
	}
	//析构函数
	~AVLTree()
	{
		Destroy(_root);
		_root = nullptr;
	}
	//插入
	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->_left = cur;
		}
		cur->_parent = parent;

		// 更新平衡因子
		while (parent)
		{
			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 (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)
				{
					RotateRL(parent);
				}
				else 
				{
					RotateLR(parent);
				}
				break;
			}
			else
			{
				assert(false);
			}
		}

		return true;
	}
	//查找函数
	Node* Find(const K& key)
	{
		Node* cur = _root;
		while (cur)
		{
			if (cur->_kv.first < key)
			{
				cur = cur->_right;
			}
			else if (cur->_kv.first > key)
			{
				cur = cur->_left;
			}
			else
			{
				return cur;
			}
		}

		return nullptr;
	}
	//打印树,中序
	void InOrder()
	{
		inOrder(_root);
		cout << endl;
	}
	//验证函数
	bool IsAVLTree()
	{
		return _IsAVLTree(_root);
	}
	
private:
	bool _IsAVLTree(Node* pRoot)
	{
		if (pRoot == nullptr)
		{
			return true;
		}
		if (_Height(pRoot->_right) - _Height(pRoot->_left) != pRoot->_bf || abs(_Height(pRoot->_right) - _Height(pRoot->_left)>1))
		{
			cout << pRoot->_kv.first << ":" << pRoot->_kv.second << " bf=" << pRoot->_bf << endl;
			cout << "_Height(pRoot->_right) - _Height(pRoot->_left)=" << _Height(pRoot->_right) - _Height(pRoot->_left);
			return false;
		}
		return _IsAVLTree(pRoot->_left) && _IsAVLTree(pRoot->_right);
	}
	int _Height(Node* pRoot)
	{
		if (pRoot == nullptr)
		{
			return 0;
		}

		size_t leftH = _Height(pRoot->_left);
		size_t rightH = _Height(pRoot->_right);
		return leftH > rightH ? leftH + 1 : rightH + 1;
	}
	void inOrder(Node* root)
	{
		if (root == nullptr)
		{
			return;
		}

		inOrder(root->_left);
		cout << root->_kv.first<<":"<<root->_kv.second<<" ";
		inOrder(root->_right);
	}
	//左旋
	void RotateL(Node* parent)
	{
		Node* subR = parent->_right;
		Node* subRL = subR->_left;

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

		Node* parentParent = parent->_parent;

		subR->_left = parent;
		parent->_parent = subR;

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

			subR->_parent = parentParent;
		}

		parent->_bf = subR->_bf = 0;
	}
	//右旋
	void  RotateR(Node* parent)
	{
		Node* subL = parent->_left;
		Node* subLR = subL->_right;

		parent->_left = subLR;
		if (subLR)
			subLR->_parent = parent;

		Node* parentParent = parent->_parent;

		subL->_right = parent;
		parent->_parent = subL;

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

			subL->_parent = parentParent;
		}
		parent->_bf = subL->_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 == 0)
		{
			subR->_bf = 0;
			subRL->_bf = 0;
			parent->_bf = 0;
		}
		else if (bf == 1)
		{
			subR->_bf = 0;
			subRL->_bf =0;
			parent->_bf = -1;
		}
		else if (bf == -1)
		{
			subR->_bf = 1;
			subRL->_bf = 0;
			parent->_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 == 0)
		{
			subL->_bf = 0;
			subLR->_bf = 0;
			parent->_bf = 0;
		}
		else if (bf == 1)
		{
			subL->_bf = -1;
			subLR->_bf = 0;
			parent->_bf =0;
		}
		else if (bf == -1)
		{
			subL->_bf = 0;
			subLR->_bf = 0;
			parent->_bf = 1;
		}
		else
		{
			assert(false);
		}
	}
	//摧毁树函数
	void Destroy(Node* root)
	{
		if (root == nullptr)
			return;

		Destroy(root->_left);
		Destroy(root->_right);
		delete root;
	}
	//拷贝函数
	Node* Copy(Node* root)
	{
		if (root == nullptr)
			return nullptr;

		Node* newRoot = new Node(root->_key, root->_value);
		newRoot->_left = Copy(root->_left);
		newRoot->_right = Copy(root->_right);

		return newRoot;
	}

private:
	Node* _root = nullptr;
};

本文就到这里,感谢你看到这里❤️❤️! 我知道一些人看文章喜欢静静看,不评论🤔,但是他会点赞😍,这样的人,帅气低调有内涵😎,美丽大方很优雅😊,明人不说暗话,要你手上的一个点赞😘!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值