当前位置: 首页 > 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;
}

三、删除节点

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

删除规则

按搜索树删除

删除并更新平衡因子

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

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

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

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

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


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

相关文章:

  • PCA9685 一款由 NXP Semiconductors 生产的 16 通道、12 位 PWM(脉宽调制)控制器芯片
  • Python - Quantstats量化投资策略绩效统计包 - 详解
  • Python从0到100(八十六):神经网络-ShuffleNet通道混合轻量级网络的深入介绍
  • 阿里云 - RocketMQ入门
  • 进阶数据结构——高精度运算
  • AI大模型开发原理篇-4:神经概率语言模型NPLM
  • 商品列表及商品详情展示
  • 通过想像,见证奇迹
  • 【gRPC-gateway】初探grpc网关,插件安装,默认实现,go案例
  • Mysql进阶学习
  • 最新 Android 热门开源项目公布
  • 稀疏混合专家架构语言模型(MoE)
  • 【4Day创客实践入门教程】Day4 迈向高手之路——进一步学习!
  • .cc扩展名是什么语言?C语言必须用.c为扩展名吗?主流编程语言扩展名?Java为什么不能用全数字的文件名?
  • 七、深入了解SpringBoot的配置文件
  • 代随(138):单调栈:一维接雨水
  • 如何将IP切换到海外:详细指南
  • WebSocket使用及优化(心跳机制与断线重连)_websocket timeout
  • IT运维的365天--025 H3C交换机用NTP同步正确的时间
  • PyDeequ库在AWS EMR启动集群中数据质量检查功能的配置方法和实现代码
  • FreeRTOS学习 --- 列表和列表项
  • 数据结构初探:链表之双向链表篇
  • C#面试常考随笔7:什么是匿名⽅法?还有Lambda表达式?
  • Python之如何在Visual Studio Code 中写的python程序打包成可以在Windows系统下运行的.exe程序
  • Vue 3.0打造响应式用户界面的新方式
  • 智慧园区平台系统在数字化转型中的作用与应用前景探究