当前位置: 首页 > article >正文

AVL树介绍

一、介绍

高度平衡的搜索二叉树,保证每个节点的左右子树高度差不超过1,降低搜索树的高度以提高搜索效率。

通过平衡因子和旋转来保证左右子树高度差不超过1

二、插入节点

1、插入规则

(1)搜按索树规则插入节点

(2)更新父节点平衡因子

插入左边就 --,插入右边就 ++

(3)保证父节点平衡因子不超过1

平衡因子是0:表示插入节点之后以父节点为子树的树高度没变,所以插入合法直接跳出。

平衡因子是正负1:以父节点为子树的树高度增大或者减小了1,虽然当前的父节点依旧合法,但是由于高度变了还要向上更新平衡因子。

平衡因子是正负2:要旋转使以父节点为子树的树平衡因子合法。合法后直接挑出。

2、旋转逻辑实现

(1)左高右低 右单旋

旋转前:

旋转后:

大逻辑:把 subLR 给 parent,把 parent 给 subL

右单旋代码实现逻辑和注意点:

a、标记三个节点:subL,subLR,ppNode(保存 parent 的父亲,为了最后更新 subL 的父节点)

b、先 subLR 作 parent 的左子树,注意更新subLR 父节点时 subLR == nullptr 情况

c、再 parent 作 subL 的右子树,注意更新 parent 的父节点

d、利用 ppNode 更新subL 的位置和父节点,注意一开始父节点是根的情况

e、更新 subL 和 parent 的平衡因子为0

右单旋代码:

void RotateR(Node* parent)
{
	Node* subL = parent->_left;
	Node* subLR = subL->_right;
	Node* ppNode = parent->_parent;

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

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

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

(2)右高左低 左单旋

旋转前:

旋转后:

分析过程和右单旋类似,代码如下:

void RotateL(Node* parent)
{
	Node* subR = parent->_right;
	Node* subRL = subR->_left;
	Node* ppNode = parent->_parent;

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

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

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

(3)左高右高 左右双旋

旋转图:

左右双旋的本质就是把 subLR 的左子树给 subL,右子树给 parent

通过本质就可以知道三种不同情况的平衡因子:

              插入前 subLR 平衡因子             插入后平衡因子
subLR subLparent 
-1(新节点插在 subLR 的左子树)001
1(新节点插在 subLR 的右子树)0-10
0(新节点就是 subLR)000

左右双旋代码:

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

	RotateL(subL);
	RotateR(parent);

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

(4)右高左高 右左双旋

旋转图:

右左双旋的本质就是把 subRL 的左子树给 parent,右子树给 subR

通过本质就可以知道三种不同情况的平衡因子:

              插入前 subRL 平衡因子             插入后平衡因子
subRL subRparent 
-1(新节点插在 subRL 的左子树)010
1(新节点插在 subRL 的右子树)00-1
0(新节点就是 subRL)000

右左双旋代码:

void RotateRL(Node* parent)
{
	Node* subR = parent->_right;
	Node* subRL = subR->_left;
	int bf = subRL->_bf;

	RotateR(subR);
	RotateL(parent);

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

3、插入代码展示

bool insert(const pair<K, V> kv)
{
	if (_root == nullptr)
	{
		_root = new Node(kv);
		return true;
	}

	Node* cur = _root;
	Node* parent = cur;
	// 1.像搜索二叉树一样插入节点cur
	while (cur)
	{
		if (cur->_kv.first > kv.first)
		{
			parent = cur;
			cur = cur->_left;
		}
		else if (cur->_kv.first < kv.first)
		{
			parent = cur;
			cur = cur->_right;
		}
		else
			return false;
	}

	cur = new Node(kv);
	if (parent->_kv.first < kv.first)
		parent->_right = cur;
	else
		parent->_left = cur;
	cur->_parent = parent;

	// 2.更新平衡因子
	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)
				RotateR(parent);
			// 右高左低左单旋
			else if (parent->_bf == 2 && cur->_bf == 1)
				RotateL(parent);
			// 左高右高左右双旋
			else if (parent->_bf == -2 && cur->_bf == 1)
				RotateLR(parent);
			// 右高左高右左双旋
			else
				RotateRL(parent); 
			break;
		}
		else
			assert(false);
	}
	return true;
}

三、验证AVL树

检查每个节点的左右子树高度差是否不超过1和平衡因子是否是左右子树的高度差

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

	int leftHeight = _Height(root->_left);
	int rightHeight = _Height(root->_right);
	// 不平衡
	if (abs(leftHeight - rightHeight) >= 2)
	{
		cout << root->_kv.first << endl;
		return false;
	}

	// 顺便检查一下平衡因子是否正确
	if (rightHeight - leftHeight != root->_bf)
	{
		cout << root->_kv.first << endl;
		return false;
	}

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

四、删除节点

这里只了解删除逻辑,具体如何旋转不考虑。

删除规则

按搜索树删除

删除并更新平衡因子

删除左边就 ++,删除右边就 --

保证父节点平衡因子不超过1

平衡因子是0:表示删除结点之前是正负1,删除后高度改变,所以要向上继续更新。

平衡因子是正负1:表示删除结点之前是0,删除后高度不变,所以跳出。

平衡因子是正负2:要旋转使以父节点为子树的树平衡因子合法。合法后直接跳出。


http://www.kler.cn/a/527786.html

相关文章:

  • 实验一---典型环节及其阶跃响应---自动控制原理实验课
  • jEasyUI 创建 CRUD 应用
  • 【搞定offer】远程医疗:健康科技领域,搞定医疗offer
  • Fiddler(一) - Fiddler简介_fiddler软件
  • 最近最少使用算法(LRU最近最少使用)缓存替换算法
  • 计算机网络一点事(22)
  • Java设计模式:行为型模式→观察者模式
  • LeetCode-180. 连续出现的数字
  • 吉首市城区地图政府附近1公里范围高清矢量pdf\cdr\ai内容测评
  • TCP三次握手和四次挥手面试题
  • WordPress eventon-lite插件存在未授权信息泄露漏洞(CVE-2024-0235)
  • DFS(深度优先搜索)与回溯算法详解
  • LLMs之WebRAG:STORM/Co-STORM的简介、安装和使用方法、案例应用之详细攻略
  • 芯片AI深度实战:给vim装上AI
  • Vue 3 30天精进之旅:Day 10 - Vue Router
  • 计算机毕业设计Python+CNN卷积神经网络考研院校推荐系统 考研分数线预测 考研推荐系统 考研爬虫 考研大数据 Hadoop 大数据毕设 机器学习
  • LeetCode--84. 柱状图中最大的矩形【单调栈】
  • OpenCV:闭运算
  • C++11(中)
  • 5 长度和距离计算模块(length.rs)
  • 《苍穹外卖》项目学习记录-Day7导入地址簿模块功能代码
  • SSM开发(十) SSM框架协同工作原理
  • 菜鸟之路Day13一一方法引用
  • Flutter Candies 一桶天下
  • Windows系统中Docker可视化工具对比分析,Docker Desktop,Portainer,Rancher
  • python中字典用法