【C++】AVL树的插入实现(详解旋转机制)

news2024/11/20 8:42:00

作者阿润菜菜
📖专栏C++


文章目录

  • AVL树的定义
  • AVL树的旋转机制
    • 1.左旋操作 --- 新节点插入较高右子树的右侧---右右:左单旋
    • 2.右旋操作 --- 新节点插入较高左子树的左侧——左左:右单旋
    • 3.左右双旋 --- 新节点插入较高左子树的右侧——左右:先左单旋再右单旋
    • 4.右左双旋 --- 新节点插入较高右子树的左侧——右左:先右单旋再左单旋
    • 5. 双旋操作中平衡因子的调节
  • AVL树的插入实现


AVL树的定义

一棵AVL树是其每个结点的平衡因子绝对值最多相差1的二叉查找树。
平衡因子?这是什么东西,别急,继续往下看。

先给出对应平衡因子的计算,我们这里是根节点的右子树高度减去左子树高度。同时平衡因子的绝对值越大,说明树越不平衡。当平衡因子的绝对值大于1时,就需要进行旋转操作来恢复平衡

当然更好一点的定义是每个结点的左子树和右子树的高度差。如果用右子树的高度减去左子树的高度来计算平衡因子,那么需要注意平衡因子的符号和旋转方向会相反。这是因为,如果你按照定义来计算平衡因子,那么当平衡因子为正时,说明左子树比右子树高,此时需要进行右旋或左右双旋;当平衡因子为负时,说明右子树比左子树高,此时需要进行左旋或右左双旋。但是如果你用右子树的高度减去左子树的高度来计算平衡因子,那么当平衡因子为正时,说明右子树比左子树高,此时需要进行左旋或右左双旋;当平衡因子为负时,说明左子树比右子树高,此时需要进行右旋或左右双旋。也就是说,平衡因子的符号和旋转方向会相反。

根据定义这颗二叉排序树中有结点的平衡因子的绝对值超过1,就不是一颗AVL树。

在这里插入图片描述
例如上图:在插入90结点之前是一颗标准的AVL树,在插入90结点之后就不是了,我们查找一下最小不平衡二叉排序树,从距离90结点最近的结点开始,80结点平衡因子为1,70结点平衡因子为2,到这里就找到了,所以以50为根结点的子树就是最小不平衡二叉排序树
明白了以上概念后我们就需要再了解一下左旋与右旋的概念了。AVL树的精华部分就是通过旋转来保持一颗二叉平衡搜索树👇👇👇

AVL树的旋转机制

首先初步认识一下AVL树的四种旋转:
AVL的四种旋转是指在插入或删除节点后,为了保持AVL树的平衡性,需要对树进行调整的操作。
AVL旋转的名称一般是根据新节点插入的位置和最先失衡的节点的位置来命名的。

这么说清楚些

  • 左单旋(RR):当新节点插入到较高右子树的右侧时,需要对最先失去平衡的节点进行一次左旋使其右子树成为新的根节点,原根节点成为其左子树
  • 右单旋(LL):当新节点插入到较高左子树的左侧时,需要对最先失去平衡的节点进行一次右旋使其左子树成为新的根节点,原根节点成为其右子树
  • 左右双旋(LR):当新节点插入到较高左子树的右侧时,需要先对左子树进行一次左旋,再对最先失去平衡的节点进行一次右旋使其左子树的右子树成为新的根节点
  • 右左双旋(RL):当新节点插入到较高右子树的左侧时,需要先对右子树进行一次右旋,再对最先失去平衡的节点进行一次左旋使其右子树的左子树成为新的根节点

然后:
旋转的原则:保持此棵树继续是二叉搜索树
旋转的目的:保持左右均衡,降低整棵树的高度

1.左旋操作 — 新节点插入较高右子树的右侧—右右:左单旋

在这里插入图片描述

  1. 在较高右子树右侧插入节点后,节点30的平衡因子变为2,这时我们将30节点进行左旋,这样AV了树就重新达到平衡了

下面展示代码基本上都加上了注释,对比上面左旋流程仔细分析一下,这里需要注意一下,旋转完后结点的父节点都需要重置。

代码示例:左旋操作

void RotateL(Node* parent) // 左旋操作
{
	Node* subR = parent->_right; // subR 是 parent 的右子树根节点
	Node* subRL = subR->_left; // subRL 是 subR 的左子树根节点

	parent->_right = subRL; // 将 subRL 作为 parent 的右子树
	if (subRL)
		subRL->_parent = parent; // 如果 subRL 存在,更新其父节点为 parent

	Node* ppnode = parent->_parent; // ppnode 是 parent 的父节点

	subR->_left = parent; // 将 parent 作为 subR 的左子树
	parent->_parent = subR; // 更新 parent 的父节点为 subR

	if (ppnode == nullptr) // 如果 ppnode 不存在,说明 parent 是根节点
	{
		_root = subR; // 更新根节点为 subR
		_root->_parent = nullptr; // 根节点的父节点为空
	}
	else // 如果 ppnode 存在
	{
		if (ppnode->_left == parent) // 如果 parent 是 ppnode 的左子树
		{
			ppnode->_left = subR; // 将 subR 作为 ppnode 的左子树
		}
		else // 如果 parent 是 ppnode 的右子树
		{
			ppnode->_right = subR; // 将 subR 作为 ppnode 的右子树
		}

		subR->_parent = ppnode; // 更新 subR 的父节点为 ppnode
	}

	parent->_bf = subR->_bf = 0; // 更新 parent 和 subR 的平衡因子为 0
}

2.右旋操作 — 新节点插入较高左子树的左侧——左左:右单旋

在这里插入图片描述

  1. 新节点插入较高左子树的左侧时,节点60的平衡因子变为2,这里我们将节点60进行右旋,就变成右图的一颗AVL树了
void RotateR(Node* parent) // 右旋操作,和左旋操作对称,只需将左右互换即可
{
	Node* subL = parent->_left; // subL 是 parent 的左子树根节点
	Node* subLR = subL->_right; // subLR 是 subL 的右子树根节点

	parent->_left = subLR; // 将 subLR 作为 parent 的左子树
	if (subLR)
		subLR->_parent = parent; // 如果 subLR 存在,更新其父节点为 parent

	Node* ppnode = parent->_parent; // ppnode 是 parent 的父节点

	subL->_right = parent; // 将 parent 作为 subL 的右子树
	parent->_parent = subL; // 更新 parent 的父节点为 subL

	if (parent == _root) // 如果 parent 是根节点
	{
		_root = subL; // 更新根节点为 subL
		_root->_parent = nullptr; // 根节点的父节点为空
	}
	else // 如果 ppnode 存在
	{
		if (ppnode->_left == parent) // 如果 parent 是 ppnode 的左子树
		{
			ppnode->_left = subL; // 将 subL 作为 ppnode 的左子树
		}
		else // 如果 parent 是 ppnode 的右子树
		{
			ppnode->_right = subL; // 将 subL 作为 ppnode 的右子树
		}
		
        subL->_parent = ppnode; // 更新subL的父节点为ppnode;
    }

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

3.左右双旋 — 新节点插入较高左子树的右侧——左右:先左单旋再右单旋

上面就是左旋与右旋的操作,这部分搞明白之后理解双旋就容易了。
在这里插入图片描述
关于双旋的代码: 旋转的代码其实并不需要我们怎么写,我们直接复用左右单旋的代码即可,但在传参时要注意轴点的位置变化,拿右左双旋来举例,轴点先为subR后为parent。
请先看双旋操作中平衡因子的调节

void RotateLR(Node* parent)//LR型,先对parent的左子树进行左旋,再对parent进行右旋;
{
    Node*subL=parent->left;//subL是parent的左子树根节点;
    Node*subLR=subL->right;//subLR是subL的右子树根节点;
    int bf=subLR->bf;//记录subLR的平衡因子;

    RotateL(parent->left);//对parent的左子树进行左旋;
    RotateR(parent);//对parent进行右旋;

    if(bf==1)//如果subLR的平衡因子为1;
    {
        parent->bf=0;//更新parent的平衡因子为0;
        subLR->bf=0;//更新subLR的平衡因子为0;
        subL->bf=-1;//更新subL的平衡因子为-1;
    }
    else if(bf==-1)//如果subLR的平衡因子为-1;
    {
        parent->bf=1;//更新parent的平衡因子为1;
        subLR->bf=0;//更新subLR的平衡因子为0;
        subL->bf=0;//更新subL的平衡因子为0;
    }
    else if(bf==0)//如果subLR的平衡因子为0;
    {
        parent->bf=0;//更新parent的平衡因子为0;
        subLR->bf=0;//更新subLR的平衡因子为0;
        subL->bf=0;//更新subL的平衡因子为0;
    }
    else//其他情况不可能发生;
    {
        assert(false);
    }
}

4.右左双旋 — 新节点插入较高右子树的左侧——右左:先右单旋再左单旋

在这里插入图片描述

void RotateRL(Node* parent)//RL型,先对parent的右子树进行右旋,再对parent进行左旋;
{
    Node*subR=parent->right;//subR是parent的右子树根节点;
    Node*subRL=subR->left;//subRL是subR的左子树根节点;
    int bf=subRL->bf;//记录subRL的平衡因子;

    RotateR(parent->right);//对parent的右子树进行右旋;
    RotateL(parent);//对parent进行左旋;

    if(bf==0)//如果subRL的平衡因子为0;
    {
        parent->bf=subR->bf=subRL->bf=0;//更新三个结点的平衡因子都为0;
    }
    else if(bf==1)//如果subRL的平衡因子为1;
    {
        parent->bf=-1;//更新parent的平衡因子为-1;
        subR->bf=subRL->bf=0;//更新subR和subRL的平衡因子都为0;
    }
    else if(bf==-1)//如果subRL的平衡因子为-1;
    {
        subR->bf=1;//更新subR的平衡因子为1;
        parent->bf=subRL->bf=0;//更新parent和subRL的平衡因子都为0;
    }
    else//其他情况不可能发生;
    {
        assert(false);
    }
}

5. 双旋操作中平衡因子的调节

关于双旋的代码实现,它主要的难点不是旋转,因为旋转我们直接复用单旋代码就可以处理了,真正的难点是在平衡因子的调节这块儿,他又分了三种情况
在这里插入图片描述

总结:
假如以pParent为根的子树不平衡,即pParent的平衡因子为2或者-2,分以下情况考虑

  1. pParent的平衡因子为2,说明pParent的右子树高,设pParent的右子树的根为pSubR
    当pSubR的平衡因子为1时,执行左单旋
    当pSubR的平衡因子为-1时,执行右左双旋
  2. pParent的平衡因子为-2,说明pParent的左子树高,设pParent的左子树的根为pSubL
    当pSubL的平衡因子为-1是,执行右单旋
    当pSubL的平衡因子为1时,执行左右双旋
    旋转完成后,原pParent为根的子树个高度降低,已经平衡,不需要再向上更新。

AVL树的插入实现

先给出AVL树的基本框架:

//AVL树的节点
template<class K, class V>//直接设置成KV模型,不搞K模型了
	class AVLTreeNode
	{
	public:
		AVLTreeNode(const std::pair<K, V>& p) :
			_data(p),
			_left(nullptr),
			_right(nullptr),
			_parent(nullptr),
			_bf(0)
		{}
		std::pair<K, V> _data;//K、V关系封装在一起
		int _bf;//平衡因子
		AVLTreeNode<K, V> *_left;//
		AVLTreeNode<K, V> *_parent;//用于记录当前节点的父节点
		AVLTreeNode<K, V> *_right;
	};
	template<class K, class V=bool>
	class AVLTree
	{
		typedef AVLTreeNode<K, V> Node;//节点
	public:
		AVLTree() :_root(nullptr)
		{}
	private:
	//在private方法这里定义旋转操作的方法
	Node* _root;
	};

步骤如下:

  1. 按照二叉搜索树的规则,从根节点开始比较,找到合适的位置插入新节点。
  2. 从插入节点向上回溯,更新每个节点的高度和平衡因子。
  3. 如果发现某个节点的平衡因子的绝对值大于1,说明该节点失去平衡,需要进行旋转操作。
  4. 根据失去平衡的节点和其子节点的平衡因子,判断旋转的类型(LL,RR,LR或RL)。
  5. 根据旋转的类型,进行相应的单旋或双旋操作,调整节点的位置和指针。
  6. 在旋转后,再次更新相关节点的高度和平衡因子。
  7. 返回新的根节点或子树的根节点。

整体参考代码:

bool insert(const std::pair<K, V>& data)
{
	Node* parent = nullptr;
	Node* cur = _root;
	if (_root == nullptr)//空树
	{
		_root = new Node(data);
		return true;
	}
	while (cur)
	{
		parent = cur;
		if (cur->_data.first == data.first)
			return false;
		else if (cur->_data.first > data.first)
			cur = cur->_left;
		else
			cur = cur->_right;
	}
	cur = new Node(data);
	cur->_parent = parent;//记住要维护父指针域
	if (parent->_data.first > cur->_data.first)
		parent->_left = cur;
	else
		parent->_right = cur;
	//插入一个节点过后,必定导致,cur这个节点的祖宗节点的平衡因子改变,我们需要跟新cur的父节点的平衡因子
	while (parent)
	{
	//cur是parent的右孩子,parent右孩子_bf++
		if (cur->_data.first > parent->_data.first)
			parent->_bf++;
		else
			parent->_bf--;
		//检查更新过后的parent的平衡因子,然后做出不同的反应
		if (parent->_bf == 0)
		{
			//更新过后parent的平衡因子为0,那么更新之前parent的平衡因子一定是 ±1 (可列方阵=程验证);
			//这说明,新插入的节点,是插在parent矮的一颗子树上的,parent这棵树的高度不变,无需在更新祖宗节点的平衡因子;
			//此次插入,非常成功
			break;
		}
		else if (abs(parent->_bf) == 1)
		{
			//更新过后parent的平衡因子是 ±1 那么说明更新之前parent的_bf一定是0
			//这说明在此次插入过后,parent这棵树的高度增加了,必须更新祖宗节点的_bf
			parent = parent->_parent;//parent、cur直接往上更新
		}
		else if (abs(parent->_bf) == 2)
		{
			//更新过后parent的平衡因子是 ±2 ,那么说明更新之前parent的_bf一定是±1;
			//这说明,此时parent这课树已经失衡了,需要旋转parent这课树
			//不平衡又有四种情况,这四种情况,对应不同的旋转方法

			//这里有个小细节,就是要先比较parent->_bf,再比较parent->_left/parent->_right,不能交换比较顺序,不然会出现错误
			if (parent->_bf == 2 && parent->_right->_bf == 1)//(RR型)
			{
				//parent左旋
				RotateL(parent);
			}
			else if (parent->_bf == -2 && parent->_left->_bf == -1)//(LL型)
			{
				//parent右旋
				RotateR(parent);
			}
			else if (parent->_bf == 2 && parent->_right->_bf == -1)//(RL型)
			{
				//先记录一下parent的右节点的左节点的_bf
				Node* SubR = parent->_right;
				Node* SubRL = SubR->_left;//放心这个节点一定存在,可证明
				int bf = SubRL->_bf;
				//1、先parent->_right右旋
				RotateR(parent->_right);
				//2、再parent左旋
				RotateL(parent);
				//这里我们需要重新更新一下平衡因子,因为单单的左旋、右旋只会将平衡因子置0,这是不正确的,需要我们手动调节
				if (bf == -1)
				{
					SubRL->_bf = 0;
					parent->_bf = 0;
					SubR->_bf = 1;
				}
				else if (bf == 1)
				{
					SubRL->_bf = 0;
					SubR->_bf = 0;
					parent->_bf = -1;
				}
				else if (bf == 0)
				{
					SubRL ->_bf= 0;
					SubR ->_bf= 0;
					parent->_bf = 0;
				}
				else
				{
					assert(false);
				}
			}
			else if (parent->_bf == -2 && parent->_left->_bf == 1)//(LR型)
			{
				Node* SubL = parent->_left;
				Node* SubLR = SubL->_right;//放心这个节点一定存在,可证明
				int bf = SubLR->_bf;
				//1、先parent->_left左旋
				RotateL(parent->_left);
				//2、再parent右旋
				RotateR(parent);
				//平衡因子调节
				if (bf == -1)
				{
					SubLR->_bf = 0;
					SubL->_bf = 0;
					parent->_bf = 1;
				}
				else if (bf == 1)
				{
					SubLR->_bf = 0;
					SubL->_bf = -1;
					parent->_bf = 0;
				}
				else if (bf == 0)
				{
					SubLR->_bf = 0;
					SubL->_bf = 0;
					parent->_bf = 0;
				}
				else
				{
					assert(false);
				}
			}
			else
			{
			//不用说了,插入之前的AVL树就已经出问题了
				assert(false);
			}
			//无论哪种旋转,旋转完毕过后,头结点平衡因子都为0了,
			//这课树已经平衡了,不需要在向上调整了
			break;
		}
		else
		{
//如果更新完过后parent->_bf等于1、-1、0、-2、2之外的数,不用说了,插入之前的AVL树就已经出问题了
     	assert(false);
		}
	}
	return true;
}
void RotateR(Node* parent)
{
	Node* SubL = parent->_left;
	Node* SubLR = SubL->_right;
	Node* ppNode = parent->_parent;

	parent->_left = SubLR;
	if (SubLR)//SubLR节点存在
		SubLR->_parent = parent;

	SubL->_right = parent;
	parent->_parent = SubL;
	if (ppNode == nullptr)//parent是根节点
	{
		SubL->_parent = nullptr;
		_root = SubL;
	}
	else
	{
		SubL->_parent = ppNode;
		if (ppNode->_data.first > SubL->_data.first)
			ppNode->_left = SubL;
		else
			ppNode->_right = SubL;
	}
	SubL->_bf = 0;
	parent->_bf = 0;
}
void RotateL(Node* parent)
{
	Node* SubR = parent->_right;//这个节点一定存在,可以证明
	Node* SubRL = parent->_right->_left;//这个节点就不一定存在了
	Node* ppNode = parent->_parent;//提前记录一下parent的父亲
	//开始链接SubRL节点
	parent->_right = SubRL;
	if (SubRL)//只有当这个节点存在时,才需要维护器=其父亲节点
		SubRL->_parent = parent;
	//开始链接parent节点
	SubR->_left = parent;
	parent->_parent = SubR;

	//开始链接SubR节点
	if (ppNode == nullptr)//如果parent就是根,那么需要更新根节点
	{
		SubR->_parent = nullptr;
		_root = SubR;
	}
	else//parent不是根节点
	{
		SubR->_parent = ppNode;
		if (ppNode->_data.first > SubR->_data.first)
			ppNode->_left = SubR;
		else
			ppNode->_right = SubR;
	}
	//更新平衡因子
	SubR->_bf = 0;
	parent->_bf = 0;
}

AVL树是一棵绝对平衡的二叉搜索树,其要求每个节点的左右子树高度差的绝对值都不超过1,这样可以保证查询时高效的时间复杂度,即 l o g 2 ( N ) log_2 (N) log2(N)。但是如果要对AVL树做一些结构修改的操作,性能非常低下,比如:插入时要维护其绝对平衡,旋转的次数比较多,更差的是在删除时,有可能一直要让旋转持续到根的位置。因此:如果需要一种查询高效且有序的数据结构,而且数据的个数为静态的(即不会改变),可以考虑AVL树,但一个结构经常修改,就不太适合

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

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

相关文章

JMeter开发自动化接口测试脚本练习

一、打开浏览器代理服务器设置 我这里用的是360浏览器&#xff0c;打开浏览器代理服务器设置&#xff0c;端口要与jmeter中的端口设置保持一致哦。 二、JMeter设置代理 JMeter设置代理&#xff08;jmeter中的端口要与360浏览器端口设置保持一致哦。&#xff09; 三、启动代理运…

BM6 判断链表中是否有环

判断链表中是否有环_牛客题霸_牛客网 (nowcoder.com) 双指针&#xff0c;快指针一次走两步&#xff0c;慢指针一次走一步&#xff0c;快指针不为空且快指针的下一个指针不为空的情况下 若快慢指针相遇即位有环。 /** * Definition for singly-linked list. * struct ListNode {…

基于RK3588的8K智能摄像机方案设计

设计了一款基于石墨烯散热的8 K智能摄像头&#xff0c;主控采用瑞芯微RK3588&#xff0c;传感器采用索尼IMX435&#xff0c; 通过HDMI2.1将传感器采集到的图像发送到8 K显示器&#xff0c;实现端到端的8 K呈现&#xff0c;为了确保摄像头性能稳定&#xff0c;本 设计采用石墨烯…

ETL到底是什么?

各位数据的朋友&#xff0c;大家好&#xff0c;我是老周道数据&#xff0c;和你一起&#xff0c;用常人思维数据分析&#xff0c;通过数据讲故事。 前段时间和大家聊了一个话题&#xff0c;就是为什么要用构建数据仓库&#xff0c;而不是直连数据源的方式开发报表&#xff1f;通…

七、MyBatis自定义映射resultMap

文章目录 七、自定义映射resultMap7.1 resultMap处理字段和属性的映射关系7.2 多对一映射处理级联方式处理映射关系使用association处理映射关系分步查询 7.3 一对多映射处理collection分步查询 本人其他相关文章链接 七、自定义映射resultMap 注意&#xff1a;下面两行表看看…

公司新来的00后真是卷王,工作没2年,跳槽到我们公司起薪18K都快接近我了

说00后躺平了&#xff0c;但是有一说一&#xff0c;该卷的还是卷。这不&#xff0c;前段时间我们公司来了个00后&#xff0c;工作都没两年&#xff0c;跳槽到我们公司起薪18K&#xff0c;都快接近我了。后来才知道人家是个卷王&#xff0c;从早干到晚就差搬张床到工位睡觉了。 …

分布式光伏发电大规模应用,运维难题如何解?

国家能源局数据显示&#xff0c;2022年我国光伏新增装机达 87.4GW&#xff0c;同比59%&#xff0c;其中&#xff1a;集中式装机达36.29GW&#xff0c;同比41.8%&#xff1b;分布式装机达51.11GW&#xff0c;同比207.9%&#xff0c;已连续两年超过集中式电站。 近年来&#xff…

如何在Windows系统中恢复丢失的分区?

有些时候&#xff0c;您突然发现自己的分区丢失&#xff0c;并且无法在Windows文件资源管理器中看到它&#xff0c;进入磁盘管理工具&#xff0c;丢失的分区也将被显示为额外的未分配空间&#xff0c;而不是原始分区。如果您遇到了与上述案例类似的情况&#xff0c;某个分区丢失…

AntDB数据库受邀参加第六届上海人工智能大会,分享AIGC时代核心交易系统升级方案

近日&#xff0c;第六届上海人工智能大会春季论坛圆满落幕。大会以“数智互联&#xff0c;瞰见未来”为主题&#xff0c;邀请了来自国内外十余个国家和地区的学术界顶级学者和业内知名企业的技术大咖&#xff0c;探讨人工智能的学术、人才、技术、行业发展痛点。亚信科技AntDB数…

新闻月刊 | GBASE 4月市场动态一览

产品动态 4月&#xff0c;GBASE南大通用大规模分布式并行数据库GBase 8a MPP Cluster中标人保财险“2022年基础软件产品及服务采购”项目。这是自2019年GBASE与人保财险达成合作以来支持建设的第三期项目。项目上线后&#xff0c;将极大满足人保财险大数据中心及研发中心的增量…

学网络安全怎么挖漏洞?怎么渗透?

前言 有不少阅读过我文章的伙伴都知道&#xff0c;我从事网络安全行业已经好几年&#xff0c;积累了丰富的经验和技能。在这段时间里&#xff0c;我参与了多个实际项目的规划和实施&#xff0c;成功防范了各种网络攻击和漏洞利用&#xff0c;提高了安全防护水平。 也有很多小…

css div上下左右排序

<!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>div上下左右排序</title> </head> <style>.div-box {display: grid;grid-auto-flow: column; /* 排序方式&#xff1a; column-先列…

编译链接再认识+gdb认识+makefile了解+缓冲区的理解+进度条的实现

索引 一. 编译链接再认识1.预处理2.编译3.汇编4.链接1.静态链接2.动态链接 二.gdb三.makefile/make四.缓存区的理解五. 进度条的实现 一. 编译链接再认识 主要针对gcc展开 一个文件从源文件编译成可执行文件大致要经历四个步骤 预处理&#xff08;进行宏替换&#xff09;编译…

office@word官方文档查看@审阅@批注@修订

文章目录 office官方文档microsoft office 文档教程语言切换文档官网word官方培训资源找到合适的文档 word共享共同创作的相关支持word审阅重点修订批注审阅窗格右侧边窗格修订选项区分标记和修订 officeword官方文档查看审阅批注修订 office官方文档 microsoft office 文档教…

国家信息安全水平考试中NISP三级(专项)网络安全证书介绍

国家信息安全水平考试中NISP三级&#xff08;专项&#xff09;网络安全证书介绍 ​1、什么是NISP? 国家信息安全水平考试&#xff08;National Information Security Test Program&#xff0c;简称NISP&#xff09;&#xff0c;是由中国信息安全测评中心实施培养国家网络空间…

c++ 11标准模板(STL) std::vector (六)

定义于头文件 <vector> template< class T, class Allocator std::allocator<T> > class vector;(1)namespace pmr { template <class T> using vector std::vector<T, std::pmr::polymorphic_allocator<T>>; }(2)(C17…

I/O常用扩展方法与芯片

主要有四种I/O扩展方法&#xff1a; (1)并行总线扩展的方法 (2)串行口扩展方法 (3)I/O端口模拟串行方法 (4)通过单片机内I/O的扩展方法 IO口扩展可以通过以下芯片来实现&#xff1a; 1、并行扩展芯片&#xff0c;比如8255 &#xff0c;8155等。 2、锁存器或缓冲器来扩展&#x…

README.md编写

一、摘要 项目一般会有个描述文件&#xff0c;对于项目的代码来讲&#xff0c;这个描述就是README.md文件&#xff0c;可以描述各模块功能、目录结构等。该文件可以方便让人快速了解项目的代码结构和功能。当然&#xff0c;若要深层次的了解项目&#xff0c;就得看项目总体的需…

Postman(接口测试工具)使用教程

目录 Postman(接口测试工具) Postman 介绍 Postman 相关资源 Postman 安装 具体安装步骤 ● 安装 Postman 快速入门 快速入门-实现步骤 其它说明 Postman(接口测试工具) Postman 介绍 1. Postman 是一款功能超级强大的用于发送 HTTP 请求的 测试工具 2. 做 WEB 页面开…

(MAX5048BAUT+T)ASEMI代理美信MAX5048BAUT+T车规级芯片

编辑-Z MAX5048BAUTT特征&#xff1a; 型号&#xff1a;MAX5048BAUTT 可控上升和下降时间的独立源和汇输出 4V至12.6V单电源 7.6A/1.3A峰值吸收/源极驱动电流 0.23Ω 开路漏极N沟道吸收输出 2.Ω 漏极开路P通道源极输出 12ns&#xff08;典型&#xff09;传播延迟 反相…