【C++练级之路】【Lv.15】AVL树(双子旋转,领略绝对平衡之美)

这篇具有很好参考价值的文章主要介绍了【C++练级之路】【Lv.15】AVL树(双子旋转,领略绝对平衡之美)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。


【C++练级之路】【Lv.15】AVL树(双子旋转,领略绝对平衡之美),进击的C++,数据结构世界,c++,开发语言,AVL树,数据结构,深度优先

快乐的流畅:个人主页
个人专栏:《C语言》《数据结构世界》《进击的C++》
远方有一堆篝火,在为久候之人燃烧!

引言

之前讲解了二叉搜索树,最优情况下它具有非常好的搜索性能,但是在极端场景下,它可能退化为单支树,可以形象地称为歪脖子树~

【C++练级之路】【Lv.15】AVL树(双子旋转,领略绝对平衡之美),进击的C++,数据结构世界,c++,开发语言,AVL树,数据结构,深度优先
这样的话,它搜索的时间复杂度会从O(log2N)退化为O(N2),完全丧失了其优良的搜索性能。那么AVL树就可以登场了,它就是为解决这类问题而生的!

一、AVL树的概念

两位俄罗斯的数学家G.M.Adelson-Velskii和E.M.Landis在1962年发明了AVL树,AVL树满足两条性质:

  • 它的左右子树都是AVL树
  • 任意结点的左右子树高度差的绝对值不超过1

这样通过控制子树高度差,让AVL树几乎完美接近于平衡,便不会出现单支树的情况,保证了优良的搜索性能,因此AVL树又称为高度平衡二叉搜索树

二、AVL树的模拟实现

2.1 结点

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

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

细节:

  1. 使用三叉链,增加了指向parent的指针
  2. 使用KV模型,数据存储键值对pair
  3. 结点存储平衡因子,用来记录左右子树高度差

注:平衡因子计算高度差,是 右子树高度 - 左子树高度

2.2 成员变量

template<class K, class V>
class AVLTree
{
protected:
	typedef AVLTreeNode<K, V> Node;
public:
protected:
	Node* _root = nullptr;
};

2.3 插入

因为AVL树也是二叉搜索树,所以默认成员函数和遍历与之前写的没什么不同,这里重点讲解AVL树的插入。

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->_right)
		{
			++parent->_bf;
		}
		else
		{
			--parent->_bf;
		}

		if (parent->_bf == 1 || parent->_bf == -1)
		{
			parent = parent->_parent;
			cur = cur->_parent;
		}
		else if (parent->_bf == 0)
		{
			break;
		}
		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)
			{
				RotateLR(parent);
			}
			else if (parent->_bf == 2 && cur->_bf == -1)
			{
				RotateRL(parent);
			}
			else
			{
				assert(false);
			}
			break;
		}
		else
		{
			assert(false);
		}
	}

	return true;
}

思路:

  1. 以二叉搜索树的方式正常插入
  2. 讨论平衡因子,以及调整结构

这里的重点在于如何讨论和调整平衡因子(bf)。

  1. 首先,插入cur结点,调整parent结点的bf,左减右加
  2. 讨论parent的bf
    • bf为0
    • bf为1或-1
    • bf为2或-2

bf为0时:
【C++练级之路】【Lv.15】AVL树(双子旋转,领略绝对平衡之美),进击的C++,数据结构世界,c++,开发语言,AVL树,数据结构,深度优先
分析:此时没有增加高度,而是补上缺口,整棵树是平衡的,直接break即可


bf为1或-1时:
【C++练级之路】【Lv.15】AVL树(双子旋转,领略绝对平衡之美),进击的C++,数据结构世界,c++,开发语言,AVL树,数据结构,深度优先
分析:此时增加了局部子树的高度,不确定有没有影响整体的高度差,所以要继续向上调整

parent = parent->_parent;
cur = cur->_parent;

bf为2或-2时:

此时bf已经超出平衡限制区间,需要进行结构调整,我们称之为旋转

2.4 旋转

旋转分为两大类:单旋和双旋。而单旋分为左单旋和右单旋,双旋分为左右旋和右左旋。

2.4.1 左单旋

【C++练级之路】【Lv.15】AVL树(双子旋转,领略绝对平衡之美),进击的C++,数据结构世界,c++,开发语言,AVL树,数据结构,深度优先

场景:右部外侧插入

过程:

  1. parent接过subR的左子树subRL
  2. subR左边再链接parent

void RotateL(Node* parent)//左单旋
{
	Node* grandparent = parent->_parent;
	Node* subR = parent->_right;
	Node* subRL = subR->_left;

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

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

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

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

细节:

  1. 大体是三步链接,注意双向链接
  2. 注意判空(subRL,grandparent)
  3. 如果判空,注意_root的传递
  4. 最后调整平衡因子_bf

2.4.2 右单旋

【C++练级之路】【Lv.15】AVL树(双子旋转,领略绝对平衡之美),进击的C++,数据结构世界,c++,开发语言,AVL树,数据结构,深度优先

场景:左部外侧插入

过程:

  1. parent接过subL的右子树subLR
  2. subL右边再链接parent

void RotateR(Node* parent)//右单旋
{
	Node* grandparent = parent->_parent;
	Node* subL = parent->_left;
	Node* subLR = subL->_right;

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

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

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

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

细节:同左单旋

2.4.3 左右旋

【C++练级之路】【Lv.15】AVL树(双子旋转,领略绝对平衡之美),进击的C++,数据结构世界,c++,开发语言,AVL树,数据结构,深度优先

场景:左部内侧插入

过程:

  1. 先对subL进行左单旋
  2. 再对parent进行右单旋

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 = -1;
		subLR->_bf = 0;
		parent->_bf = 0;
	}
	else if (bf == -1)
	{
		subL->_bf = 0;
		subLR->_bf = 0;
		parent->_bf = 1;
	}
	else if (bf == 0)
	{
		subL->_bf = 0;
		subLR->_bf = 0;
		parent->_bf = 0;
	}
	else
	{
		assert(false);
	}
}

细节:

  1. 这里旋转直接复用前面单旋的代码
  2. 主要的重点,在于平衡因子bf的讨论
    • bf为1,在subLR的右侧插入
    • bf为-1,在subLR的左侧插入
    • bf为0,插入subLR(之前为空)

2.4.4 右左旋

【C++练级之路】【Lv.15】AVL树(双子旋转,领略绝对平衡之美),进击的C++,数据结构世界,c++,开发语言,AVL树,数据结构,深度优先

场景:右部内侧插入

过程:

  1. 先对subR进行右单旋
  2. 再对parent进行左单旋

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

	RotateR(subR);
	RotateL(parent);

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

细节:同左右旋


综上所述,旋转的目的保证平衡,同时降低树的高度

三、AVL树的验证

我们主要验证左右子树高度是否平衡,即高度差是否小于等于1

bool IsBalance()
{
	return _IsBalance(_root);
}

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

	int leftH = Height(root->_left);
	int rightH = Height(root->_right);

	return leftH > rightH ? leftH + 1 : rightH + 1;
}

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

	int leftH = Height(root->_left);
	int rightH = Height(root->_right);

	if (rightH - leftH != root->_bf)
	{
		cout << root->_kv.first << "节点平衡因子异常" << endl;
		return false;
	}

	return abs(rightH - leftH) <= 1
		&& _IsBalance(root->_left)
		&& _IsBalance(root->_right);
}

细节:

  1. 为了方便计算高度,写一个Height函数
  2. 在子函数递归中,计算高度差是否小于等于1
  3. 与此同时,还要检查平衡因子是否正常

四、AVL树的性能

4.1 优势

AVL树是一棵绝对平衡的二叉搜索树,其要求每个节点的左右子树高度差的绝对值都不超过1,这样可以保证查询时高效的时间复杂度,即 l o g 2 ( N ) log_2 (N) log2(N)

4.2 缺陷

但是如果要对AVL树做一些结构修改的操作,性能非常低下,比如:插入时要维护其绝对平衡,旋转的次数比较多,更差的是在删除时,有可能一直要让旋转持续到根的位置。

4.3 适用场景

因此,如果需要一种查询高效且有序的数据结构,而且数据的个数为静态的(即不会改变),可以考虑AVL树,但一个结构经常修改,就不太适合。


【C++练级之路】【Lv.15】AVL树(双子旋转,领略绝对平衡之美),进击的C++,数据结构世界,c++,开发语言,AVL树,数据结构,深度优先文章来源地址https://www.toymoban.com/news/detail-843258.html

真诚点赞,手有余香

到了这里,关于【C++练级之路】【Lv.15】AVL树(双子旋转,领略绝对平衡之美)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【C++练级之路】【Lv.14】二叉搜索树(进化的二叉树——BST)

    快乐的流畅:个人主页 个人专栏:《C语言》《数据结构世界》《进击的C++》 远方有一堆篝火,在为久候之人燃烧! 二叉树在之前的数据结构章节讲解过,当时使用C来实现。而如今学习的二叉搜索树,便是二叉树的进阶,也更适合使用C++来实现。 二叉搜索树(BST,Binary Se

    2024年03月23日
    浏览(33)
  • 【C++练级之路】【Lv.16】红黑树(冰与火的碰撞,红与黑的史诗)

    快乐的流畅:个人主页 个人专栏:《C语言》《数据结构世界》《进击的C++》 远方有一堆篝火,在为久候之人燃烧! 之前学习的AVL树,是一种平衡二叉搜索树,它追求绝对平衡,从而导致插入和删除性能较差。而今天学习的红黑树,是另一种平衡二叉搜索树,它追求相对平衡

    2024年04月09日
    浏览(62)
  • 【C++练级之路】【Lv.20】位图和布隆过滤器(揭开大数据背后的神秘面纱)

    快乐的流畅:个人主页 个人专栏:《算法神殿》《数据结构世界》《进击的C++》 远方有一堆篝火,在为久候之人燃烧! 哈希映射 的思想,在实际中有许多运用,之前介绍的 哈希表 是一种经典的应用场景,而今天我们将了解其他的哈希数据结构—— 位图和布隆过滤器 ,它

    2024年04月12日
    浏览(50)
  • 【C++练级之路】【Lv.4】类和对象(下)(初始化列表,友元,static成员,编译器的优化)

    欢迎各位小伙伴关注我的专栏,和我一起系统学习C++,共同探讨和进步哦! 学习专栏 : 《进击的C++》 在创建对象时,编译器通过调用构造函数,给对象中各个成员变量一个合适的初始值。 虽然上述构造函数调用之后,对象中已经有了一个初始值,但是不能将其称为对对象

    2024年02月04日
    浏览(58)
  • 【C++练级之路】【Lv.3】类和对象(中)(没掌握类的6个默认成员函数,那你根本就没学过C++!)

    欢迎各位小伙伴关注我的专栏,和我一起系统学习C++,共同探讨和进步哦! 学习专栏 : 《进击的C++》 在C++的学习中,类和对象章节的学习尤为重要,犹如坚固的地基,基础不牢,地动山摇;而默认成员函数的学习,在类和对象的学习里最为重要。所以要 学好C++,学好默认

    2024年02月04日
    浏览(49)
  • 【C++练级之路】【Lv.2】类和对象(上)(类的定义,访问限定符,类的作用域,类的实例化,类的对象大小,this指针)

    欢迎各位小伙伴关注我的专栏,和我一起系统学习C++,共同探讨和进步哦! 学习专栏 : 《进击的C++》 C语言是 面向过程 的,关注的是过程,分析出求解问题的步骤,通过函数调用逐步解决问题。 C++是基于 面向对象 的,关注的是对象,将一件事情拆分成不同的对象,靠对象

    2024年02月03日
    浏览(48)
  • C++ AVL树(四种旋转,插入)

    AVL树又称高度平衡二叉搜索树,它的高度接近log[2]N(以2为底N的对数),整棵树的形状接近完全二叉树 增删查改的时间复杂度是O(log[2]N) 本节我们实现的是Key-Value模型的AVL树 这里我们的AVL树节点比起普通的二叉树的节点来说多了两个成员 第一个是平衡因子,这里我们定义的平衡因子

    2024年02月04日
    浏览(42)
  • 【C++】详解AVL树的旋转及其插入操作

    二叉搜索树虽然可以缩短查找的效率,但 如果数据有序或接近有序二叉搜索树将退化为单支树,查找元素相当于在顺序表中搜索元素,效率低下 。因此,两位俄罗斯的数学家G.M.Adelson-Velskii和E.M.Landis在1962年发明了一种解决上述问题的方法: 当向二叉搜索树中插入新节点后,

    2024年02月15日
    浏览(45)
  • 【C++打怪之路Lv1】-- C++开篇(入门)

    🌈 个人主页: 白子寰 🔥 分类专栏: C++打怪之路,python从入门到精通,魔法指针,进阶C++,C语言,C语言题集,C语言实现游戏 👈 希望得到您的订阅和支持~ 💡 坚持创作博文(平均质量分82+),分享更多关于深度学习、C/C++,python领域的优质内容!(希望得到您的关注~) 目

    2024年04月23日
    浏览(38)
  • 【C++打怪之路Lv3】-- 类和对象(上)

    🌈 个人主页:白子寰 🔥 分类专栏: C++打怪之路,python从入门到精通,数据结构,C语言,C语言题集 👈 希望得到您的订阅和支持~ 💡 坚持创作博文(平均质量分82+),分享更多关于深度学习、C/C++,python领域的优质内容!(希望得到您的关注~)    目录 面向对象和面向过程

    2024年04月27日
    浏览(49)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包