AVL树(平衡二叉树)的介绍以及相关构建

news2024/11/18 4:49:06

欢迎光临 :          羑悻的小杀马特-CSDN博客

目录

一·AVL树的介绍:

二·AVL树的实现:

1·结构框架:

2·节点的插入: 

旋转: 

2·1左单旋:

2.1.1左单旋介绍及步骤:

2.1.2左单旋代码实现:

2.1.3左单旋技巧总结: 

2·2右单旋:

2.2.1右单旋介绍及步骤:

2.2.2右单旋代码实现:

2.2.3右单旋技巧总结:  

2·3左右双旋 :

2.3.1左右双旋介绍及步骤:

2.3.2左右双旋代码实现:

 2.3.3左右双旋技巧总结:  

2.4右左双旋:

2.4.1右左双旋介绍及步骤:

2.4.2右左双旋代码实现:

  2.4.3右左双旋技巧总结:  

旋转系列的总结:

3.节点的查找:

4.检验AVL树是否平衡: 

三·AVL树的代码汇总: 


一·AVL树的介绍:

AVL树,它的左右⼦树都是AVL树,且左右⼦树的⾼度差的绝对值不超过1。AVL树是⼀颗⾼度平衡搜索⼆叉树, 通过控制⾼度差去控制平衡。

因此为了能记录它是否平衡这里引入了一个新名词也就是平衡因子:balance factor:某个结点的平衡因子就是它右子树的高度减去左子树的高度,也就是说这个树要是AVL树那么它的平衡因子一定是-1或0或1,否则就要旋转调整(后面会介绍到)。

那为啥高度差不能都为0呢?如⼀棵树是2个结点,4个结点等情况下,⾼度差最好就是1,⽆法作为⾼度差是0,因此这样设计是比较合理的

当然因为这样设计就趋近于完全二叉树,那么高度就可以理解为log(n),那么此时它的增删查改也可以这么认为成log(n)。

二·AVL树的实现:

1·结构框架:

template<class K, class V>
struct AVLTreeNode
{
	// 需要parent指针,后续更新平衡因子可以看到
	pair<K, V> _kv;
	AVLTreeNode<K, V>* _left;
	AVLTreeNode<K, V>* _right;
	AVLTreeNode<K, V>* _parent;

	int _bf; // balance factor

	AVLTreeNode(const pair<K, V>& kv)
		:_kv(kv)
		, _left(nullptr)
		, _right(nullptr)
		, _parent(nullptr)
		, _bf(0)
	{}
};
template<typename K,typename V>
class AVLTree {
public:
	using node = AVLTreeNode<K, V>;
private:
	node* _root = nullptr;
};

2·节点的插入: 

这里插入其实和上次写的二叉搜索树的插入差不多,但是就多了控制高度平衡以及对平衡因子的控制操作。

也就是我们找到空位置连接插入后,然后把它的parent处节点的平衡因子更新一下,看一下是否符合条件:然后分情况决定继续向上遍历查找还是停止还是旋转。下面分为三种情况:

1·情况一:恰好为0,也就是原来是-1或者1变来的,此时就不需要向上调整了,为什么呢?因为未插入前parent作为一个节点的右孩子或者左孩子,那么这个节点的假设是左孩子是parent,可以发现此时插入还是不插入这个节点的平衡因子都不变故这时就不需要向上判断。

如:

2·情况二:恰好为1或者-1,也就是从-2或者0变来的,那么此时它变化了,可能会导致上面变化因此还要向上继续遍历找平衡因子的变化。

3·情况三:恰好是-2或者是2,那么此时就不符合平衡规则那就涉及到旋转了(下面会讲到)。

旋转: 

这里旋转是为了什么呢?1·保持搜索树的原则。2·可以降低树的高度。3·可以保证树的平衡。,因此根据parent为2或者-2,以及它的左或右孩子为1或-1的几种情况可以把它分为左单旋,右单旋,左右双旋,右左双旋等。

2·1左单旋:

2.1.1左单旋介绍及步骤:

左单旋肯定是右边高,这里为了方便对下面很多节点,这里直接把剩下的子树抽象化,因为它是平衡二叉树故因此可以把再你插入之前,将要变成2或者-2的节点作为parent以及它的下面分割下来成为抽象的树部分如:

这样的话,左单旋也就是在最右边的h(当然这里的h>=0)高度的下面插入一个节点, 然后通过左单旋变为平衡状态:

这里我们可以看出来,就是把pr的左指针指向parent,parent的右指针指向pr1,但是这里就忽视了最终要的父亲指针,此时也要注意,把pr1(注意是否为空)的父亲指针指向parent,然后保存原先parent的父亲指针(也要判断一下parent是否为根节点从而单独做处理),然后把此时的连接连成pr即可了。

2.1.2左单旋代码实现:

下面展示一下代码:

void RotateL(node* parent) {//左旋
	node* subr = parent->_right;
	node* subrl = subr->_left;
	//处理sublr:
	parent->_right = subrl;
	if (subrl) subrl->_parent = parent;//sublr为空不能访问

	node* pp = parent->_parent;//保存parent的父节点指针
	//调节新旧“parent”位置:
	subr->_left = parent;
	parent->_parent = subr;

	if (pp == nullptr) {
		_root = subr;
		subr->_parent = nullptr;
	}
	else {
		if (parent == pp->_left) pp->_left = subr;
		else pp->_right = subr;
		subr->_parent = pp;
	}
	parent->_bf = subr->_bf = 0;


}

2.1.3左单旋技巧总结: 

技巧总结:这里对于左单旋的操作进行一下总结:把parent节点向下压,然后把 pr的左孩子也就是prl作为parent右孩子,其他注意连接即可。

 

2·2右单旋:

2.2.1右单旋介绍及步骤:

右单旋肯定是左边高,其实也是根左单旋大差不差,下面上图:

这里也和上面类似只不过是把parent的左指针指向plr,pl的右指针指向parent,还有一些其他的注意连接方法,右单旋也就是在最左边的h插入使它变为h+1:

这里与左单旋大差不差,因此就不重复了。

2.2.2右单旋代码实现:

代码:

void RotateR(node* parent) {//右旋
	node* subl = parent->_left;
	node* sublr = subl->_right;

	parent->_left = sublr;
	if (sublr) sublr->_parent = parent;

	node* pp = parent->_parent;
	subl->_right = parent;
	parent->_parent = subl;
	if (pp == nullptr) {
		_root = subl;
		subl->_parent = nullptr;
	}
	else {
		if (parent == pp->_left) pp->_left = subl;
		else pp->_right = subl;
		subl->_parent = pp;
	}
	parent->_bf = subl->_bf = 0;
}

2.2.3右单旋技巧总结:  

技巧总结:把parent向下压,然后把plr与parent的左指针相连,然后注意parent指针的控制即可。 

2·3左右双旋 :

2.3.1左右双旋介绍及步骤:

这里为什么叫这个呢?其实它的步骤可以分为先对parent的孩子进行左单旋,然后再对parent进行右单旋,即可。这里我们可以这么理解,即就是在进行右单旋的插入图,把节点插在中间的h上即可,而这时根据h>=0;分为了三种情况,对应的是平衡因子的变化:

情况一:就是h为0的情况:那么此时到最后进行旋转后它们的平衡因子都是0。

情况二:插入h的右边,那么旋转后,pl的平衡因子就是-1,其他都是零。

情况三:插入h的左边,那么旋转后parent的平衡因子就是1。

那么我们写代码的时候怎么区分这三种情况就成了写左右双旋的关键了,即这时候我们得到的插入后的plr这个节点的bf一定是如果是0,就是第一种情况,如果是-1就是第三种情况 ,如果是1就是第二种情况,因此可以根据这个进行代码的编写。

步骤1:首先先把pl左旋,然后parent进行右旋,这时大概结构就搞好了,也就是高度OK了,步骤2:接下来就是平衡因子的更改,可以根据我们旋转之前保存的pl的bf分情况进行对旋转后parent,pl和plr三个节点处平衡因子进行更新操作。

2.3.2左右双旋代码实现:

代码展示:

void	RotateLR(node* parent) {//左右双旋
	node* subl = parent->_left;
	node* sublr = subl->_right;
	int bf = sublr->_bf;
	RotateL(subl);
	RotateR(parent);
	if (bf == -1) {//插入的是sublr的左支
		sublr->_bf = 0;
		subl->_bf = 0;
		parent->_bf = 1;

	}
	else if (bf == 1) {//插入的是sublr的右支
		sublr->_bf = 0;
		subl->_bf = -1;
		parent->_bf = 0;

	}
	else if (bf == 0) {//插入前sublr为空
		sublr->_bf = 0;
		subl->_bf = 0;
		parent->_bf = 0;
	}
	else
	{
		assert(false);
	}


}

 2.3.3左右双旋技巧总结:  

技巧总结:就是把parent节点压下来,plr节点提到最上面,之后pl连接它的左指针,parent连接它的右指针,然后plr的左孩子给pl右指针,右孩子给parent左指针。

2.4右左双旋:

2.4.1右左双旋介绍及步骤:

这里其实和左右双旋一样,但是是左旋最初的那个图在中间的h插入,然后也是分为那三种情况。

情况1:h=0,最后插入的就是prl这个节点,那么此时parent,prl和pr平衡因子都是0.

情况2:h>=1,但是插入的那个h的右边,那么此时,pr的bf是0,parent的bf是-1,然后prl的平衡因子就是0。

情况3: h>=1,但是插入的那个h的左边,那么此时,pr的bf是1,parent的bf是0,然后prl的平衡因子就是0。

跟左右双旋一样,那么我们写代码的时候怎么区分这三种情况就成了写右左双旋的关键了,即这时候我们得到的插入后的plr这个节点的bf一定是如果是0,就是第一种情况,如果是-1就是第三种情况 ,如果是1就是第二种情况,因此可以根据这个进行代码的编写。

步骤1:类似,还是pr右旋,然后parent左旋,链接好形状。

步骤2:更新平衡因子,如果第一种情况,此时parent,pr,prl的平衡因子都是0,第二种情况,prl是0,parent是-1,pr是0,第三种情况:prl还是0,parent是0,pr是1。

2.4.2右左双旋代码实现:

代码:

void RotateRL(node* parent) {//右左双旋
	node* subr = parent->_right;
	node* subrl = subr->_left;
	int bf = subrl->_bf;
	RotateR(subr);
	RotateL(parent);
	if (bf == -1) { //插入的是subrl的左支
		subrl->_bf = 0;
		subr->_bf = 1;
		parent->_bf = 0;

	}
	else if (bf == 1) {//插入的是subrl的右支
		subrl->_bf = 0;
		subr->_bf = 0;
		parent->_bf = -1;

	}

	else if (bf == 0) {//插入前subrl为空
		subrl->_bf = 0;
		subr->_bf = 0;
		parent->_bf = 0;
	}
	else
	{
		assert(false);
	}


}

  2.4.3右左双旋技巧总结:  

 技巧总结:就是把parent节点压下来,prl节点提到最上面,之后parent连接它的左指针,pr连接它的右指针,然后prl的左孩子给parent右指针,右孩子给pr左指针。

旋转系列的总结:

这里对上面四种旋转方式如何进行的做一个总结:

1·首先是左单旋和右单旋:可以这么想向哪一边旋转就是另一边高,故就是插入的是最边上才导致增高的,根据此画出相应的图来,都是parent被拽下来,然后少的孩子用旁边的离得最近的孩子补上,其次就是注意各个节点对应的指针的连接。

2·左右双旋和右左双旋:可以这么理解,左右:右单旋的图,只是节点插在了中间的h处(再分三种情况判断平衡因子)右左:左单旋的图,节点插在了中间的h;操作就是:plr或者prl(中间h分出去的节点或者是h=0,新插入的节点)提到最上面,然后pl成左支或者pr成右支,然后被截下来的plr或者prl的左支向左补,右支向右补,最后处理好节点之间的连接即可。

3.节点的查找:

node* Find(const K& key)
{
	node* cur = _root;
	while (cur)
	{
		if (cur->_kv.first < key)
		{
			cur = cur->_right;
		}
		else if (cur->_kv.first > key)
		{
			cur = cur->_left;
		}
		else
		{
			return cur;
		}
	}
	return nullptr;
}

4.检验AVL树是否平衡: 

导致其不平衡的条件有两个:要么高度不符合要求,要么就是高度正确而平衡因子未更新正确。

因此可以根据这两个条件来写代码完成检验操作。

int treehight(node* root) {
	if (root == nullptr)  return 0;
	int lefthight = treehight(root->_left);
	int righthight = treehight(root->_right);
	return lefthight > righthight ? lefthight + 1 : righthight + 1;
}

bool _IsBalanceTree(node* root) {//不平衡肯定是在插入数据的时候没有更新正确导致:
	//如高度没有控制好,或者就是高度控制了当往上调整的时候平衡因子没有及时更新导致平衡因子不符合要求
	if (root == nullptr) return true;

	int lefthight = treehight(root->_left);
	int righthight = treehight(root->_right);
	int gap = abs(lefthight - righthight);
	if (gap >= 2)
	{
		cout << root->_kv.first << ":高度存在问题" << endl;
		return false;
	}
	if (abs(root->_bf) != gap) {
		cout << root->_kv.first << ":平衡因子存在问题"<< endl;
		return false;
	}
	return _IsBalanceTree(root->_left) && _IsBalanceTree(root->_right);

}

这里顺便说一下删除操作:如果没找到此节点就直接返回了,要么就是删除的是叶子节点直接删除就可,要么是删除一个节点,它的左孩子为空或者右孩子为空,那么此时删除这个节点后,把另一端不为空的孩子补过去,最后一种就是要删除节点的 左右孩子都不为空,此时就要类似二叉搜索树删除一样找替代的孩子,即此节点右孩子一直向左遍历找到最后一个节点与它替换在完成间接删除操作。这里就不做操作了,大致就是这个意思。

三·AVL树的代码汇总: 

#pragma once
#include<iostream>
#include<assert.h>
#include<string>
#include<vector>
#include<algorithm>
using namespace std;
template<class K, class V>
struct AVLTreeNode
{
	// 需要parent指针,后续更新平衡因子可以看到
	pair<K, V> _kv;
	AVLTreeNode<K, V>* _left;
	AVLTreeNode<K, V>* _right;
	AVLTreeNode<K, V>* _parent;

	int _bf; // balance factor

	AVLTreeNode(const pair<K, V>& kv)
		:_kv(kv)
		, _left(nullptr)
		, _right(nullptr)
		, _parent(nullptr)
		, _bf(0)
	{}
};
template<typename K,typename V>
class AVLTree {
public:
	using node = AVLTreeNode<K, V>;

	

	bool Insert(const pair<K, V>& kv)
	{
		if (_root == nullptr) {
			_root = new node(kv);
			return true;
		}
		else {
			//寻找空位置进行插入:
			node* cur = _root;
			node* parent = nullptr;
			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 (kv.first < parent->_kv.first) parent->_left = cur;
			else parent->_right = cur;
			cur->_parent = parent;
			//更新平衡因子:平衡更新完要么找到parent的bf等于0,要么找到了root发现它的平衡因子还是1或-1
			//保持parent始终是cur的父亲
			while (parent) {//cur仍旧是当前插入的节点
				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 = cur->_parent;
				}
				else if (parent->_bf == 2 || parent->_bf == -2) {
					//判断如何旋转:
					if (parent->_bf == 2 && parent->_right ->_bf== -1) {
						RotateRL(parent);
					}
					else if (parent->_bf == -2 && parent->_left->_bf == 1) {
						RotateLR(parent);
					}
					else if (parent->_bf == 2 && parent->_right->_bf == 1) {
						RotateL(parent);
					}
					else if (parent->_bf == -2 && parent->_left->_bf ==-1) {
						RotateR(parent);
					}

					else {
						assert(false);
					}
					break;//这里由于给它旋转后肯定保证了parent出平衡因子为0,故不用上调了

				}
				else assert(false);
			}
			return true;

		}

	}


	node* Find(const K& key)
	{
		node* cur = _root;
		while (cur)
		{
			if (cur->_kv.first < key)
			{
				cur = cur->_right;
			}
			else if (cur->_kv.first > key)
			{
				cur = cur->_left;
			}
			else
			{
				return cur;
			}
		}
		return nullptr;
	}

	int treehight(node* root) {
		if (root == nullptr)  return 0;
		int lefthight = treehight(root->_left);
		int righthight = treehight(root->_right);
		return lefthight > righthight ? lefthight + 1 : righthight + 1;
	}
	
	void InOrder() {
		_inorder(_root);
	}
	bool IsBalanceTree() {
		 return _IsBalanceTree(_root);
	}
private:

	void RotateL(node* parent) {//左旋
		node* subr = parent->_right;
		node* subrl = subr->_left;
		//处理sublr:
		parent->_right = subrl;
		if (subrl) subrl->_parent = parent;//sublr为空不能访问

		node* pp = parent->_parent;//保存parent的父节点指针
		//调节新旧“parent”位置:
		subr->_left = parent;
		parent->_parent = subr;

		if (pp == nullptr) {
			_root = subr;
			subr->_parent = nullptr;
		}
		else {
			if (parent == pp->_left) pp->_left = subr;
			else pp->_right = subr;
			subr->_parent = pp;
		}
		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* pp = parent->_parent;
		subl->_right = parent;
		parent->_parent = subl;
		if (pp == nullptr) {
			_root = subl;
			subl->_parent = nullptr;
		}
		else {
			if (parent == pp->_left) pp->_left = subl;
			else pp->_right = subl;
			subl->_parent = pp;
		}
		parent->_bf = subl->_bf = 0;
	}

	void	RotateLR(node* parent) {//左右双旋
		node* subl = parent->_left;
		node* sublr = subl->_right;
		int bf = sublr->_bf;
		RotateL(subl);
		RotateR(parent);
		if (bf == -1) {//插入的是sublr的左支
			sublr->_bf = 0;
			subl->_bf = 0;
			parent->_bf = 1;

		}
		else if (bf == 1) {//插入的是sublr的右支
			sublr->_bf = 0;
			subl->_bf = -1;
			parent->_bf = 0;

		}
		else if (bf == 0) {//插入前sublr为空
			sublr->_bf = 0;
			subl->_bf = 0;
			parent->_bf = 0;
		}
		else
		{
			assert(false);
		}


	}

	void RotateRL(node* parent) {//右左双旋
		node* subr = parent->_right;
		node* subrl = subr->_left;
		int bf = subrl->_bf;
		RotateR(subr);
		RotateL(parent);
		if (bf == -1) { //插入的是subrl的左支
			subrl->_bf = 0;
			subr->_bf = 1;
			parent->_bf = 0;

		}
		else if (bf == 1) {//插入的是subrl的右支
			subrl->_bf = 0;
			subr->_bf = 0;
			parent->_bf = -1;

		}

		else if (bf == 0) {//插入前subrl为空
			subrl->_bf = 0;
			subr->_bf = 0;
			parent->_bf = 0;
		}
		else
		{
			assert(false);
		}


	}



	void _inorder(node* root) {
		if (root == nullptr) return;
		_inorder(root->_left);
		cout << root->_kv.first << ":" << root->_kv.second << endl;
		_inorder(root->_right);

		
	}

	bool _IsBalanceTree(node* root) {//不平衡肯定是在插入数据的时候没有更新正确导致:
		//如高度没有控制好,或者就是高度控制了当往上调整的时候平衡因子没有及时更新导致平衡因子不符合要求
		if (root == nullptr) return true;

		int lefthight = treehight(root->_left);
		int righthight = treehight(root->_right);
		int gap = abs(lefthight - righthight);
		if (gap >= 2)
		{
			cout << root->_kv.first << ":高度存在问题" << endl;
			return false;
		}
		if (abs(root->_bf) != gap) {
			cout << root->_kv.first << ":平衡因子存在问题"<< endl;
			return false;
		}
		return _IsBalanceTree(root->_left) && _IsBalanceTree(root->_right);

	}
	node* _root = nullptr;
};

 

 这些也仅是个人对AVL有限的理解,希望大佬多多指导。

 

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

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

相关文章

【JavaSE系列】IO流

目录 前言 一、IO流概述 二、IO流体系结构 三、File相关的流 1. FileInputStream 2. FileOutputStream 3. FileReader 4. FileWriter 四、缓冲流 五、转换流 1. InputStreamReader 2. OutputStreamWriter 六、数据流 七、对象流 八、打印流 九、标准输入输出流…

C++学习9.28

1> 创建一个新项目&#xff0c;将默认提供的程序都注释上意义 por QT core gui #QT表示引入的类库 core:核心库例如IO操作在该库中 gui:图形化显示库 #如果要使用其他类库中的相关函数&#xff0c;就需要调用相关类库后&#xff0c;才能加以使用greaterThan(Q…

c++926

1.什么是虚函数&#xff1f;什么是纯虚函数&#xff1f; 虚函数&#xff1a;被virtual关键字修饰的成员函数&#xff0c;用于实现多态性&#xff0c;通过基类访问派生类的函数。纯虚函数&#xff1a;在虚函数后面添加0&#xff0c;只有声明而没有实现&#xff0c;需要派生类提…

天龙八部怀旧单机微改人面桃花+安装教程+GM工具+虚拟机一键端

今天给大家带来一款单机游戏的架设&#xff1a;天龙八部怀旧单机微改人面桃花。 另外&#xff1a;本人承接各种游戏架设&#xff08;单机联网&#xff09; 本人为了学习和研究软件内含的设计思想和原理&#xff0c;带了架设教程仅供娱乐。 教程是本人亲自搭建成功的&#xf…

图说数集相等定义表明“R各元x的对应x+0.0001的全体=R“是几百年重大错误

黄小宁 设集A&#xff5b;x&#xff5d;表A各元均由x代表&#xff0c;&#xff5b;x&#xff5d;中变量x的变域是A。其余类推。因各数x可是数轴上点的坐标故x∈R变为实数yx1的几何意义可是&#xff1a;一维空间“管道”g内R轴上的质点x∈R(x是点的坐标)沿“管道”g平移变为点y…

红队信息搜集扫描使用

红队信息搜集扫描使用 红队行动中需要工具化一些常用攻击&#xff0c;所以学习一下 nmap 等的常规使用&#xff0c;提供灵感 nmap 帮助 nmap --help主机扫描 Scan and no port scan&#xff08;扫描但不端口扫描&#xff09;。-sn 在老版本中是 -sP&#xff0c;P的含义是 P…

视频美颜SDK与直播美颜工具API是什么?计算机视觉技术详解

今天&#xff0c;小编将深入探讨视频美颜SDK与直播美颜工具API的概念及其背后的计算机视觉技术。 一、视频美颜SDK的概念 视频美颜SDK是一套用于开发实时美颜效果的工具集&#xff0c;开发者可以利用它在视频流中实现面部特征的优化。这些SDK通常提供了一系列功能&#xff0c…

.NET 红队武器库和资源集合 (第38期)

01阅读须知 此文所提供的信息只为网络安全人员对自己所负责的网站、服务器等&#xff08;包括但不限于&#xff09;进行检测或维护参考&#xff0c;未经授权请勿利用文章中的技术资料对任何计算机系统进行入侵操作。利用此文所提供的信息而造成的直接或间接后果和损失&#xf…

计算机网络自顶向下(1)---网络基础

目录 1.网络的分类 2.网络协议 3.网络分层结构 1.OSI七层模型 2.TCP/IP四层模型 3.网络与OS的关系 4.网络传输基本流程 1.协议报头 5.网络中的地址管理 1.IP地址 2.端口号 6.传输层协议 1.TCP协议 2.UDP协议 3.网络字节序 7.socket 1.网络的分类 局域网&…

excel-VBA知识点记录

1、计算机硬件的组成部分 内存&#xff0c;一旦断电&#xff0c;存储在里面的数据就消失了&#xff0c;而硬盘是永久存储数据的&#xff0c;所以刚开始我们在文件里面编辑没有按保存的时候&#xff0c;数据是在内存里面的&#xff0c;一旦断电数据就没了&#xff0c;但我们点了…

大语言模型知识点分享

1 目前主流的开源模型体系有哪些&#xff1f; Prefix Decoder 系列模型 核心点&#xff1a; 输入采用双向注意力机制&#xff0c;输出为单向注意力。双向注意力意味着输入的每个部分都可以关注到输入的所有其他部分&#xff0c;这在理解上下文时具有很强的优势。 代表模型&a…

六级翻译 高分笔记

第一节 句子的拆分与重组 核心原则&#xff1a;拆主干&#xff0c;补修饰 一、句子的拆分与重组 1.青藏铁路是世界最高最长的高原铁路。&#xff08;“的”字前面所有去掉&#xff0c;就是句子主干&#xff09; The Qinghai-Tibet Railway is the highest and longest plate…

css 数字比汉字要靠上

这个问题通常是由于数字字体的下排的问题造成的&#xff0c;也就是数字的底部边缘位置比汉字的顶部边缘位置更靠下。为了解决这个问题&#xff0c;可以尝试以下几种方法&#xff1a; 使用CSS的vertical-align属性来调整对齐方式。例如&#xff0c;可以将数字的对齐方式设置为to…

数组的练习

1.使用函数的递归方法&#xff0c;输出给定字符串的逆序&#xff0c;如"abcdefg"&#xff0c;输出为“gfedcba”. 方法一&#xff1a;首先不采用递归的方法&#xff0c;如何完成上述要求 #include<stdio.h> #include<string.h> int main() {char arr[]…

3.数据结构与算法-基本概念和术语

数据、数据元素、数据项和数据对象 数据 数据元素 学生表-记录 数-节点 图&#xff1a;顶点 数据项 数据对象 数据对象与数据元素的关系 数据结构 数据结构的三个部分 逻辑结构的种类 存储结构分类 顺序存储结构 链式存储结构 索引存储结构 散列存储结构 数据类型和抽象数据类…

Linux中的文件缓冲区

目录 使用 dup2 系统调用 为命令行解释器添加重定向功能 理解缓冲区问题 缓存区的刷新策略 FILE的本质 尝试封装C语言的FILE 小共识&#xff1a; cd->当前路径->当前进程的工作路径->工作路径可以被修改->每个进程都有当前路径->故cd改的是子进程的路径-…

算力共享平台的控制流程,业务流程

目录 控制流程 业务流程 在提供的计算机网络系统结构示意图和描述中,我们可以区分出控制流程和业务流程的组成部分。 控制流程 控制流程主要涉及系统内部的管理、调度和监控操作,以确保系统能够按照预定的规则和策略运行。在这个例子中,控制流程可能包括但不限于以下部分…

K8S真正删除pod

假设k8s的某个命名空间如&#xff08;default&#xff09;有一个运行nginx 的pod&#xff0c;而这个pod是以kubectl run pod命令运行的 1.错误示范&#xff1a; kubectl delete pod nginx-2756690723-hllbp 结果显示这个pod 是删除了&#xff0c;但k8s很快自动创建新的pod,但是…

【C++算法】5.双指针_乘最多水的容器

文章目录 题目链接&#xff1a;题目描述&#xff1a;解法C 算法代码&#xff1a;图解&#xff1a; 题目链接&#xff1a; 11.盛最多水的容器 题目描述&#xff1a; 解法 7x749 解法一&#xff1a;暴力枚举 输入&#xff1a;[1&#xff0c;8&#xff0c;6&#xff0c;2&#xf…

大厂AI必备数据结构与算法——链表(三)详细文档

冲冲冲&#xff01;开干 神马&#xff01;神马&#xff01;神马&#xff0c;一向让我们学习起来抓耳挠腮的数据结构课程竟然也有教程&#xff1f;还那么详细&#xff1f;&#xff1f;真的假的&#xff1f; 那么好&#xff0c;胡广告诉你是假的&#xff0c;哈哈哈哈哈哈哈哈哈…