IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> 【C++】AVL树的简单实现及验证 -> 正文阅读

[C++知识库]【C++】AVL树的简单实现及验证

1、什么是AVL树?

??AVL树可以是一棵空树
??AVL树也可以是一棵具有如下性质的二叉搜索树
????①它的左右子树都是一棵AVL树
????②左右子树高度之差(平衡因子)的绝对值不能超过1

在这里插入图片描述

如果一棵二叉搜索树是高度平衡的,那么他就是AVL树。
如果他有n个结点,其高度可以保持在O(log2n),搜索时时间复杂度也就是O(log2n)

2、AVL树部分模块模拟实现

2.1 AVL树结点的定义:

AVL树本质上讲,它是一棵二叉搜索树,只不过加上了平衡因子这一限制条件。

也就是说,在插入一个新节点时,我们不仅要考虑结点的插入位置,还需要考虑插入该节点后对于树中其他结点来说,平衡因子是否需要更新。

其中,插入节点的父节点,它的平衡因子一定需要改变这就要求我们需要能够快速定位到插入节点的父节点。因此,我们对于AVL树结点的结构应定义为孩子双亲表示法

🆗,下面给出结点的基本结构:

template<class T>
struct AVLTreeNode
{
	typedef AVLTreeNode<T> Node;
	Node* _left;//左孩子
	Node* _right;//右孩子
	Node* _parent;//双亲
	T _value;
	int _bf;//结点的平衡因子
	
	AVLTreeNode(const T& value)
		:_left(nullptr)
		,_right(nullptr)
		,_parent(nullptr)
		,_value(value)
		,_bf(0)
	{}
}

2.2 AVL树的插入

AVL树的插入可以分为两大步骤:

①按照二叉搜索树的方式插入新节点
??第一步不是本文的重点,不了解的童鞋移步至二叉搜索树

调整结点的平衡因子

1、现在我们先给出实现第一部分的核心代码:

		if (nullptr == _root)
		{
			//这是一棵空树,直接插入结点即可
			_root = new Node(value);
			return true;
		}

		//1、按照二叉搜索树的方式查找结点的插入位置
		Node* cur = _root;
		Node* parent = nullptr;
		while (cur)
		{
			if (cur->_value == value)
			{
				return false;
			}
			else if (cur->_value > value)
			{
				parent = cur;
				cur = cur->_left;
			}
			else
			{
				parent = cur;
				cur = cur->_right;
			}
		}
		Node* newNode = new Node(value);
		if (value > parent->_value)
		{
			parent->_right = newNode;
		}
		else
		{
			parent->_left = newNode;
		}
		newNode->_parent = parent;

2、更新双亲的平衡因子
分析:
??首先,新节点插入之后,其双亲结点的平衡因子一定需要跟新,因为,插入一个结点,则该节点一定会影响其双亲的左右子树高度。因此,我们首先将双亲结点的平衡因子进行更新;
在这里插入图片描述

??对parent的平衡因子更新完毕后,parent的平衡因子可能的取值是:
0 、正负1、正负2
。我们下面就这三种大情况分别讨论:

情况1:parent的平衡因子为0
??该情况说明插入之前parent的平衡因子为正负1,插入之后被调整成为0,此时满足AVL树的性质,插入成功!
在这里插入图片描述
情况2:parent的平衡因子为正负1
??该情况说明插入之前parent的平衡因子一定是0(也就是说以parent为u根的二叉树左右子树高度是一样的),
??插入后被更新为正负1,此时以parent为根的树高度增加了,势必会影响到parent上面结点的平衡因子,因此需要继续向上跟新
在这里插入图片描述

情况3:parent的平衡因子为正负2
?该种情况较为复杂。首先,我们可以知道此时parent的平衡因子违反了AVL树的特性,因此需要对齐进行旋转处理。至于如何旋转,我们分以下几种场景进行讨论:
??场景一:新节点插入在较高左子树的左侧----->右单旋
具体场景如下图所示:
在这里插入图片描述

具体操作见代码:

void RotateRight(Node* parent)
	{
		Node* subL = parent->_left;
		Node* subLR = subL->_right;

		parent->_left = subLR;
		subL->_right = parent;
		if (subLR)
		{
			subLR->_parent = parent;
		}
		//更新subL和parent的双亲
		Node* pparent = parent->_parent;
		parent->_parent = subL;
		subL->_parent = pparent;

		//处理pparent
		if (nullptr == pparent)
		{
			_root == subL;
		}
		else 
		{
			if (pparent->_left == parent)
			{
				pparent->_left = subL;
			}
			else
			{
				pparent->_right = subL;
			}
		}

		//跟新subL和parent的平衡因子
		subL->_bf = parent->_bf = 0;
		
	}

??场景二:新节点插入在较高右子树的右侧----->左单旋
在这里插入图片描述
具体操作见代码:

void RotateLeft(Node* parent)
	{
		Node* subR = parent->_right;
		Node* subRL = subR->_left;

		parent->_right = subRL;
		if (subRL)
		{
			subRL->_parent = parent;
		}
		subR->_left = parent;

		//更新parent && subR的双亲
		Node* pparent = parent->_parent;
		parent->_parent = subR;
		subR->_parent = pparent;

		//处理pparent
		if (nullptr == pparent)
		{
			_root = subR;
		}
		else
		{
			if (pparent->_left == parent)
			{
				pparent->_left == subR;
			}
			else
			{
				pparent->_right = subR;
			}
		}
		//跟新subR && parent的平衡因子
		subR->_bf = parent->_bf = 0;
	}

??场景三:新节点插入在较高左子树的右侧----->左右双旋
在这里插入图片描述
具体代码:

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


		RotateLeft(parent->_left);
		RotateRight(parent);

		if (1 == bf)
		{
			subL->_bf = -1;
		}
		else if (-1 == bf)
		{
			parent->_bf = 1;
		}
	}

??场景四:新节点插入在较高右子树的左侧----->右左双旋
此场景类比场景三,这里直接给出代码:

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

		RotateRight(parent->_right);
		RotateLeft(parent);

		if (1 == bf)
			parent->_bf = -1;
		else if (-1 == bf)
			subR->_bf = 1;
	}

2.3 AVL的验证

AVL树是在二叉搜索树的基础上增加了平衡因子,因此我们可以从两方面进行验证:
??1、验证是否是一棵二叉搜索树
思路:查看中序遍历结果,若有序,则为二叉搜索树
在这里插入图片描述

??2、验证它是否是一棵平衡树
思路:每个节点的高度差不超过1

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

		int leftHigh = GetHigh(root->_left);
		int rightHigh = GetHigh(root->_right);
		return leftHigh > rightHigh ? leftHigh + 1 : rightHigh + 1;
	}
	bool _IsAVLTree(Node* root)
	{
		if (nullptr == root)
		{
			return true;
		}
		int leftHigh = GetHigh(root->_left);
		int rightHigh = GetHigh(root->_right);
		if (rightHigh - leftHigh != root->_bf || abs(root->_bf) > 1)
		{
			cout << "Node:" << root->_value << endl;
			cout << rightHigh - leftHigh << " " << root->_bf << endl;
			return false;
		}
		// 根的左子树 和 根的右子树
		return _IsAVLTree(root->_left) &&
			_IsAVLTree(root->_right);
	}

以上就是AVL树插入模块的模拟实现与验证。具体源码请参考AVL模拟

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2022-06-06 17:10:30  更:2022-06-06 17:10:54 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 17:06:23-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码