【C++】详解AVL树并模拟实现

news2024/11/17 15:41:57

  前言:

  •   之前我们为了让数据存储效率提高,引进了二叉搜索树。
  •   但是我们发现,二叉搜索树的时间复杂度还是O(N),因为二叉搜索树并不是非常的平衡。
  •   并不是所有树都是满二叉树,可能出现单边书这样极端的情况,所以我们引进了查找效率更高的AVL树

目录

(一)AVL树的概念

(二)AVL树的模拟实现

(1)AVL树结点的定义

(2)AVL树部分功能的实现

1、查找

2、插入(重点!)

2.1插入结点后平衡因子的变化

2.2情况分析

2.3旋转操作的实现

2.3.1左单旋

2.3.2右单旋

 2.3.3左右双旋

2.3.4右左双旋

(三) 验证AVL树

1、中序遍历

2、判断树是否平衡

(四)总代码详解

 



(一)AVL树的概念

首先AVL树是一棵二叉搜索树,一棵AVL树或者是空树,或者是具有以下性质的二叉搜索树:

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

图示:

图中结点外数字代表平衡因子→右子树高度-左子树高度

AVL树又叫高度平衡二叉搜索树。

如果一棵二叉搜索树是高度平衡的,它就是AVL树。如果它有n个结点,其高度可保持在 〇(logN)搜索时间复杂度 〇(logN)。


(二)AVL树的模拟实现

(1)AVL树结点的定义

~直接实现key_value的结构 – 三叉链的形式(带父节点)

图示如下: 

其中:

_bf-----balance factor,代表平衡因子

右子树与左子树的高度差


(2)AVL树部分功能的实现

1、查找

和二叉搜索树查找功能的实现几乎一致:

从根开始比较,查找,比根大则往右边走查找,比根小则往左边走查找。
最多查找高度次,走到到空,还没找到,这个值不存在。

2、插入(重点!)

  • 这里的插入思路和二叉搜索树中插入的思路一致,找到合适的位置之后再链接

这里链接是比较容易的,但是链接之后对各个结点中的平衡因子的调整则是比较费劲。

所以重点就在于分析各种可能出现平衡因子的情况,然后调整树来解决。

2.1插入结点后平衡因子的变化

1.首先我们考察插入结点和它父亲结点之间的关系变化:

  • 当一个结点的左或者右链接了一个结点,该结点为链接结点的父节点
  • 当该父节点的右边连接上孩子时,此时该父节点的右子树比左子树高了一层,平衡因子_bf + +
  • 当该父节点的左边连接上孩子时,此时该父节点的左子树比右子树高了一层,平衡因子_bf – –


2.然后我们再以此为基础考察其他结点的变化情况:

我们以上图为例,8结点作为父亲结点,平衡因子发生了变化,这就有可能继续导致8结点的父亲结点的平衡因子发生变化。

简而言之,插入一个结点真正会影响的是其祖先的平衡因子的改变。


3.处理方法

(1)向上更新:

  • 更新新插入节点祖先的平衡因子
  • 没有违反规则就结束了,违反规则,不平衡了就需要处理
  • 这里的处理是旋转处理(接下来会重点介绍)
  • 在更新的过程中只要是发现违反了AVL树规则的就需要旋转处理

(2)如何向上更新:

  • 更新的方式是沿着祖先路径更新(回溯)
  • 将parent结点更新到它的_parent位置上,将cur结点更新到它的_parent位置上
  • 在这个过程中一旦发现有违反AVL树规则的时即parent的平衡因子变成2或-2
  • 这时就需要进行旋转处理

具体过程如下:

  • 子树高度变了,就要继续往上更新
  • 子树的高度不变, 则更新完成
  • 子树违反平衡规则,则停止更新, 旋转子树

2.2情况分析

1.父亲结点平衡因子为0时,符合规则,break跳出:

2.父亲结点平衡因子是1或者-1时继续向上更新:

3.更新过程中,父亲结点平衡因子出现了2或者-2,说明子树不平衡了,需要上述说到的旋转处理(下文中会详解旋转的几种情况)

看了如上的代码有点抽象,我们下面用具体例子来讲解此情况下不同的情景:

我们也可以明晰的看出上述代码有四个条件语句,每个if其实代表的就是一种情景,我们详细分析:

情景一:

这时我们发现8作为父亲结点时出现了平衡因子为2的情况,此时cur结点平衡因子为1


 


情景二:

这时我们发现8作为父亲结点时出现了平衡因子为2的情况,此时cur结点平衡因子为-1


情景三,情景四大家可以自己画图啦!其实就是cur所在子树变为parent左子树的一些情况,下面详解旋转操作中将会对这几种情景解释并解决!

4.一定要检查 -- 不保证其他地方不会出现错误
            
    比如插入之前AVL数就存在平衡子树,|平衡因子| >= 2结点。


2.3旋转操作的实现

上述我们已经阐述了,在什么情况下需要对AVL树进行旋转操,接下来我们就来讲一下具体的旋转步骤。

旋转原则:

  • 保持搜索树的规则
  • 子树变平衡

旋转一共分为四种旋转方式:

  •  左单旋、右单旋
  •  左右双旋、右左双旋

2.3.1左单旋

当右子树高的时候,这时就要向左旋转。

旋转过程:

  • 将要旋转的子树的根节点设为parent,根结点的右子树为subR,subR的左节点为subRL
  • 将subRL给parent的右,再将parent给subR的左
  • 改变其链接关系即可
  • 这样一来subR做了子树的根,根结点的左右子树高度差从2变成了0

图示:

代码图解:(后续代码统一给)

2.3.2右单旋

 有左单旋的基础,我们知道右单旋的情况:
当左子树高的时候,这时就要向右旋转。

旋转过程:

  • 将要旋转的子树的根节点设为parent,根结点的左子树为subL,subL的右节点为subLR
  • 将subLR给parent的左,再将parent给subL的右
  • 改变其链接关系即可
  • 这样一来subL做了子树的根,根结点的左右子树高度差从2变成了0

图示:

代码图解:

 2.3.3左右双旋

在一些情况中,左单旋和右单旋是无法解决问题的,需要二次旋转来降低子树高度,

比如下面的情况:

这时候无论怎么单旋,我们都无法降低子树的高度。

所以这里我们要用到双旋。

图示:

这样一来,我们就把单次旋转无法降低高度的情况解决了!

详细过程可以跟着图示来理解。

代码图解: 

这里主要是根据subLR平衡因子的不同情况来给降低高度后子树中不同节点的平衡因子赋值,大家可以模仿上面的图示把不同情况画下来!

  •  我们在实现双旋的时候可以复用单旋
  •  但是单旋有个坑,会出现将平衡因子搞成0的情况

两种解决方案:

  • 将单旋中更新的平衡因子拿出来
  • 旋转之前将位置记录下来

我们采用第一种方法,单独将平衡因子拿出来处理。

2.3.4右左双旋

和上面的左右双旋的情况类似:

图示:

代码图解:
 


(三) 验证AVL树

上面我们基本完成了AVL树的两大重要的功能实现(删除操作有困难,本文暂不实现,后续深入会讨论),下面我们要验证我们写的树是不是AVL树。

主要验证两大方面:

  • 是不是二叉搜索树(AVL树是特殊的二叉搜索树,中序遍历应该是有顺序的)
  • 子树的高度差是不是符合条件(-2<右子树高度-左子树高度<2)

1、中序遍历

介绍前,我们先说明一点:

  • 我们平时调用类中的成员函数一般是对象.成员函数
  • 比如一个对象t中序历遍就是t.InOrder()
  • 这样符合库中其他类成员函数的使用习惯
  • 但是我们要想中序历遍必须传根节点
  • 所以我们叠加一层嵌套,巧妙解决这个问题:

多套一层,这样就可以符合我们使用这些接口函数的习惯了!

其实通过上面的讲解后,中序遍历作者已经给出啦:
我们选择和之前中序历遍搜索二叉树一样的方法,采用递归的方式解决

2、判断树是否平衡

我们一同思考,如何判断左右子树高度差在2以内呢?

  • 首先,我们必须知道左右子树的高度
  • 其次,我们再判断高度差是否是2以内

求树的高度:

其实我们之前也讲解过,采用递归的方法更容易来求解。

思路:树的高度就是左子树或者右子树高的那一颗树的高度加上根节点高度(也就是+1)

判断树是否平衡:

我们求解左右子树的高度后记录下来,相减的绝对值看是否是二以内。

当然,对于树中每一个结点我们都要判断他的子树是否符合条件,所以也要用到递归:

(四)总代码详解


#include<iostream>
#include<assert.h>
#include<algorithm>
using namespace std;
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;//平衡因子

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



template<class K, class V>
class AVLTree
{
	typedef AVLTreeNode<K, V> Node;
public:
	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->_left = cur;
		}
		else
		{
			parent->_right = 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)
			{
				// 需要旋转处理 -- 1、让这颗子树平衡 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);
				}
				//旋转完之后ppNode为根的子树高度不变 -- 所以对ppNode的平衡因子没有影响
				break;
			}
			else // 一定要检查 -- 不保证其他地方不会出现错误
			{
				//插入之前AVL数就存在平衡子树,|平衡因子| >= 2结点
				assert(false);
			}
		}
		return true;
	}


	void InOrder()
	{
		_InOrder(_root);
		cout << endl;
	}

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

	int Height()
	{
		return _Height(_root);
	}
private:

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

		int HeightL = _Height(root->_left);
		int HeightR = _Height(root->_right);

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

		return abs(HeightL - HeightR) < 2 && _IsBalance(root->_left) && _IsBalance(root->_right);

		空树也是AVL树
		//if (nullptr == root)
		//	return true;

		计算pRoot节点的平衡因子:即pRoot左右子树的高度差
		//int leftHeight = _Height(root->_left);
		//int rightHeight = _Height(root->_right);

		求差值
		//int diff = rightHeight - leftHeight;

		如果计算出的平衡因子与pRoot的平衡因子不相等,或者
		pRoot平衡因子的绝对值超过1,则一定不是AVL树
		//if (abs(diff) >= 2)
		//{
		//	cout << root->_kv.first << "结点平衡因子异常" << endl;
		//	return false;
		//}

		平衡因子没有异常但是和结点的对不上
		//if (diff != root->_bf)
		//{
		//	//说明更新有问题
		//	cout << root->_kv.first << "结点平衡因子不符合实际" << endl;
		//	return false;
		//}

		pRoot的左和右如果都是AVL树,则该树一定是AVL树
		把自己和自己的左右子树都检查了,递归检查
		//return _IsBalance(root->_left)
		//	&& _IsBalance(root->_right);
	}


	int _Height(Node* root)
	{
		if (root == NULL)
			return 0;

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

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



	void _InOrder(Node* root)
	{
		if (root == nullptr)
			return;

		_InOrder(root->_left);
		cout << root->_kv.first << " ";
		_InOrder(root->_right);
	}

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

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

		Node* ppnode = parent->_parent;

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

		if (ppnode == nullptr)
		{
			_root = subR;
			_root->_parent = nullptr;
		}

		else
		{
			if (ppnode->_left == parent)
			{
				ppnode->_left = subR;
			}
			else
			{
				ppnode->_right = subR;
			}

			subR->_parent = ppnode;
		}

		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* ppnode = parent->_parent;

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

		else
		{
			if (ppnode->_left == parent)
			{
				ppnode->_left = subL;
			}
			else
			{
				ppnode->_right = subL;
			}

			subL->_parent = ppnode;
		}

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

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

		RotateL(parent->_left);
		RotateR(parent);

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


	void RotateRL(Node* parent)
	{

		Node* subR = parent->_right;
		Node* subRL = subR->_left;
		int bf = subRL->_bf;

		RotateR(parent->_right);
		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);
		}
	}

private:
	Node* _root = nullptr;
};



void Test_AVLTree1()
{
	//int a[] = { 16, 3, 7, 11, 9, 26, 18, 14, 15 };
	int a[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14 };
	AVLTree<int, int> t1;
	for (auto e : a)
	{
		/*	if (e == 14)
			{
			int x = 0;
			}*/

		t1.Insert(make_pair(e, e));
		cout << e << "插入:" << t1.IsBalance() << endl;
	}

	t1.InOrder();
	cout << t1.IsBalance() << endl;
}



void Test_AVLTree2()
{
	srand(time(0));
	const size_t N = 10;
	AVLTree<int, int> t;
	for (size_t i = 0; i < N; ++i)
	{
		size_t x = rand() + i;
		t.Insert(make_pair(x, x));
		//cout << t.IsBalance() << endl;
	}

	t.InOrder();

	cout << t.IsBalance() << endl;
	cout << t.Height() << endl;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/984319.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Compose的一些小Tips - 生命周期

系列文章 Compose的一些小Tips - 生命周期&#xff08;本文&#xff09; 前言 本系列介绍Compose的一些常识&#xff0c;了解这些tips并不会让人摇身一变成为大佬&#xff0c;但可以帮助到一些学习Compose的安卓开发者避免一些误区&#xff0c;也是对入门详解中遗漏的一个补充…

智能水表远程控制系统:引领节水新时代

随着科技的不断发展&#xff0c;物联网技术逐渐融入到我们的日常生活中。其中&#xff0c;智能水表远程控制系统成为一项重要创新&#xff0c;对于提高水资源利用率、实现绿色节水具有重要意义。下面小编就来为大家介绍下智能水表远程控制系统吧! 一、智能水表远程控制系统定义…

发现一个超级神器:免费素材共享的引领者——微信公众号的“ 素材帮帮站”

博主是做软件开发的&#xff0c;拥有10年的软件开发经验&#xff0c;奈何在这个内容为王的时代&#xff0c;软件开发行业太难混了&#xff0c;在公司的安排下&#xff0c;做一些图片设计的工作。最近发现一个超级神奇&#xff0c;免费&#xff0c;全面&#xff0c;更新迅速的微…

尺度空间和使用各向异性扩散进行边缘检测——Scale-Space and Edge Detection Using Anisotropic Diffusion

0.摘要 由Witkin引入的尺度空间技术涉及使用高斯核将原始图像与卷积以生成更粗糙的分辨率图像。这种方法有一个主要缺点&#xff1a;在粗糙的尺度上准确获取“语义上有意义”的边缘位置很困难。在本文中&#xff0c;我们提出了尺度空间的新定义&#xff0c;并引入了一类利用扩散…

把握市场潮流,溯源一流品质:在抖in新风潮 国货品牌驶过万重山

好原料、好设计、好品质、好服务……这个2023&#xff0c;“国货”二字再度成为服饰行业的发展关键词。以消费热潮为翼&#xff0c;越来越多代表性品类、头部品牌展现出独特价值&#xff0c;迎风而上&#xff0c;在抖音电商掀起一轮轮生意风潮。 一个设问是&#xff1a;在抖音…

MT9700 80mΩ,可调快速响应限流配电开关芯片

MT9700 80mΩ&#xff0c;可调快速响应限流配电开关芯片 特征 符合USB规范 集成80mΩ电源MOSFET 低电源电流 15μA典型开启状态 1μA典型关闭状态 宽输入电压Range&#xff1a;2.4V到5.5V 快速瞬态响应&#xff1a;<2μs 反向电流流阻塞 热关机保护 热插件应…

音视频FFmpeg简单理解学习,必学技术

FFmpeg是一个开源的多媒体框架&#xff0c;它包含了一个用于音频和视频编解码的库。它可以执行各种多媒体操作&#xff0c;如格式转换、视频剪辑、音频处理等。可以用来记录、转换数字音频、视频&#xff0c;并能将其转化为流的开源计算机程序。 FFmpeg的结构 默认的编译会生成…

FPGA的基础架构,什么是CLB?

本原创文章由深圳市小眼睛科技有限公司创作&#xff0c;版权归本公司所有&#xff0c;如需转载&#xff0c;需授权并注明出处 CLB是指可编程逻辑功能块&#xff08;Configurable Logic Blocks&#xff09;,顾名思义就是可编程的数字逻辑电路。CLB是FPGA内的三个基本逻辑单元。C…

二叉树题目:填充每个结点的下一个右侧结点指针 II

文章目录 题目标题和出处难度题目描述要求示例数据范围进阶 解法一思路和算法代码复杂度分析 解法二思路和算法代码复杂度分析 解法三思路和算法代码复杂度分析 题目 标题和出处 标题&#xff1a;填充每个结点的下一个右侧结点指针 II 出处&#xff1a;117. 填充每个结点的下…

“系统的UI”——SystemUI

SystemUI的实现 以StatusBar为例&#xff0c;来分析下Android系统具体是如何实现它们的。 相关代码分为两部分&#xff0c;即&#xff1a; Service部分 代码路径&#xff1a;frameworks/base/services/java/com/android/server。 应用部分 代码路径&#xff1a;frameworks…

基于云计算的区域LIS系统系统源码

在医疗机构内部&#xff0c;院内实验室主要负责本院临床科室的检验&#xff0c;院内LIS系统必须满足实验室日常的标本处理入库、仪器联机、检验结果处理、报告打印、报告发布、检验信息统计、检验信息报告发布、标本流程、外部医疗机构检验报告调阅等工作。 在医疗机构间&#…

【JUC系列-04】精通Synchronized底层的实现原理

JUC系列整体栏目 内容链接地址【一】深入理解JMM内存模型的底层实现原理https://zhenghuisheng.blog.csdn.net/article/details/132400429【二】深入理解CAS底层原理和基本使用https://blog.csdn.net/zhenghuishengq/article/details/132478786【三】熟练掌握Atomic原子系列基本…

嘉泰实业举行“互联网金融知识社区”“安全理财风险讲座”等活动

每一次暖心的沟通都是一次公益,真诚不会因为它的渺小而被忽略;每一声问候都是一次公益,善意不会因为它的普通而被埋没。熟悉嘉泰实业的人都知道,这家企业不但擅长在金融理财领域里面呼风唤雨,同时也非常擅长在公益事业当中践行,属于企业的责任心,为更多有困难的群体带来大爱的传…

【数据结构】搜索树MapSet

目录 1.搜索树 1.1概念 1.2查找 1.3插入 1.4删除 2.Map 2.1map说明 2.2TreeMap和HashMap 2.3常用方法 3.Set 3.1set说明 3.2TreeSet和HashSet 3.3常用方法 1.搜索树 1.1概念 二叉搜索树又称二叉排序树&#xff0c;它或者是一棵空树&#xff0c;或者具有以下性质&…

Linux CentOS7命令及命令行

Linux CentOS7中命令及命令行是非常重要的概念。对大多数初学者来说是既熟悉又了解甚少。本文初步讨论这方面的内容&#xff0c;与同行者交流。 一、命令 命令又称为指令&#xff0c;&#xff08;英语命令 command&#xff0c;可用简写cmd表示&#xff09;&#xff0c;在终端…

爬虫逆向实战(30)-某查查股东关联公司(HmacSHA512)

一、数据接口分析 主页地址&#xff1a;某查查 1、抓包 通过抓包可以发现数据接口是api/people/getRelatCompany 2、判断是否有加密参数 请求参数是否加密&#xff1f; 无 请求头是否加密&#xff1f; 通过查看“标头”可以发现&#xff0c;请求头中有一个key和value都是…

怎么把视频转换成mp4格式

怎么把视频转换成mp4格式&#xff1f;如今&#xff0c;随着科技的不断发展&#xff0c;我们在工作中接触到的多媒体视频格式也越来越多。其中&#xff0c;MP4作为一种广泛兼容的视频格式&#xff0c;在许多软件中都能轻松播放&#xff0c;并且成为了剪辑与裁剪视频时大家常用的…

视频画质修复神器,视频修复专家告诉你怎样提高画质

如果您的视频画质模糊、失真或者过于昏暗&#xff0c;那么本文将给大家分享一个非常实用的视频修复技巧。利用一些工具增强视频细节和清晰度的高级技术&#xff0c;向您呈现最详细的视频修复教程。 修复视频画质的话也可以使用去噪滤镜和锐化滤镜。 调整视频分辨率:将视频的分…

【图文并茂】c++介绍之队列

1.1队列的定义 队列&#xff08;queue&#xff09;简称队&#xff0c;它也是一种操作受限的线性表&#xff0c;其限制为仅允许在表的一端进行插入操作&#xff0c;而在表的另一端进行删除操作 一些基础概念&#xff1a; 队尾&#xff08;rear&#xff09; &#xff1a;进行插…

C++-map和set

本期我们来学习map和set 目录 关联式容器 键值对 pair 树形结构的关联式容器 set multiset map multimap 关联式容器 我们已经接触过 STL 中的部分容器&#xff0c;比如&#xff1a; vector 、 list 、 deque 、forward_list(C11)等&#xff0c;这些容器统称为序列式…