【数据结构】平衡二叉搜索树(AVL树)——AVL树的概念和介绍、AVL树的简单实现、AVL树的增删查改

news2024/11/24 17:49:27

文章目录

  • 平衡二叉搜索树(AVL树)
    • 1.AVL树的概念和介绍
    • 2.AVL树的简单实现
      • 2.1AVL树的插入
      • 2.2AVL树的旋转
        • 2.2.1左旋
        • 2.2.2右旋
        • 2.2.3右左双旋
        • 2.2.4左右双旋
      • 全部源码

平衡二叉搜索树(AVL树)

  为什么要引入平衡二叉搜索树?

  在之前我们学习了二叉搜索树,二叉搜索树的结构类似于一个倒置的树,而左子树的值小于根节点的值,右节点的值大于根节点的值,这种结构使得二叉搜索树在处理有序数据时非常高效。但是如果在传入的数据为有序或接近有序,二叉搜索树会退化为单支树,类似链表、此时二叉搜索树在查找、插入、删除的优异性能都消失了。

  同一个关键码集合,如果各关键码插入的次序不同,可能得到不同结构的二叉搜索树:
在这里插入图片描述

  最优情况下,二叉搜索树为完全二叉树(或者接近完全二叉树),其平均比较次数为: l o g 2 N log_2 N log2N

  最差情况下,二叉搜索树退化为单支树(或者类似单支),其平均比较次数为: N 2 \frac{N}{2} 2N

             

1.AVL树的概念和介绍

  对此我们引入了平衡二叉搜索树,也叫AVL树。

  AVL树是由两位俄罗斯的数学家G. M. Adelson-VelskyE. M. Landis在1962年的论文《An algorithm for the organization of information》中发明的。这是一种自平衡二叉查找树,任何节点的两个子树的高度最大差别为1,所以它也被称为高度平衡树。

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

  (1)它的左右子树都是AVL树

  (2)左右子树高度之差(简称平衡因子)的绝对值不超过1(-1/0/1)

在这里插入图片描述

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

             

2.AVL树的简单实现

  和实现二叉搜索树的节点类似,只需要考虑多平衡因子和父子节点的关系即可。

  以下为AVL树节点的定义:

template<class K, class V>
struct AVLTreeNode
{
	pair<K, V> _kv;
	AVLTreeNode<K, V>* _left;
	AVLTreeNode<K, V>* _right;
	AVLTreeNode<K, V>* _parent;
	int _bf;   //平衡因子

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

  定义AVL树类:

template<class K,class V>
class AVLTree
{
	//便于书写Node节点
	typedef AVLTreeNode<K, V> Node;
public:
	//AVL树增删查改函数的实现
private:
	Node* _root = nullptr;
};

             

2.1AVL树的插入

  AVL树的插入操作包括插入节点和平衡调整。具体实现步骤如下:

  (1)插入节点首先,按照普通二叉搜索树的插入方法进行插入。

  (2)平衡调整插入节点后,从插入节点开始沿着通向根节点的路径向上检查所有节点,观察它们是否仍然保持平衡。如果某个节点的平衡因子绝对值大于1,就需要进行旋转操作以重新平衡这个树。旋转操作包括单旋转和双旋转。

  插入节点实现:

//AVL树插入一个节点
bool AVLInsert(const pair<K, V>& kv)
{
	//创建cur指向根节点
	Node* cur = _root;
	Node* parent = nullptr;

	//如果AVL树为空,直接返回创建的新节点
	if (_root == nullptr)
	{
		_root = new Node(kv);
		return true;
	}
	
	//如果AVL树不为空,寻找可以插入的节点
	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;

	//AVL树要保持平衡,控制平衡因子为-1、0、1
	//while()
	
	return true;
}

平衡调整实现:

新节点插入之前:

  平衡因子=右子树的高度-左子树的高度,cur插入后,parent的平衡因子一定需要调整,在插入之前,parent的平衡因子分为三种情况:-1,0, 1, 分以下两种情况:

   (1)如果cur插入到parent的左侧,左子树高+1,只需给parent的平衡因子-1即可。

   (2)如果cur插入到parent的右侧,右子树高+1,只需给parent的平衡因子+1即可。

在这里插入图片描述

while (parent)
{
	if (cur == parent->_left)//cur插入在parent左边
	{
		parent->_bf--;
	}
	else if (cur == parent->_right)//cur插入在parent右边
	{
		parent->_bf++;
	}
}

新节点插入之后:

  当cur插入以后,parent的平衡因子可能有三种情况:0,+1 \ -1, +2 \ -2

   (3)如果parent的平衡因子为0,说明插入之前parent的平衡因子为正负1,插入后被调整成0,此时满足AVL树的性质,插入成功且无需旋转。

   (4)如果pParent的平衡因子为+1 \ -1,说明插入前pParent的平衡因子一定为0,插入后被更新成正负1,此时以pParent为根的树的高度增加,需要继续向上更新,判断是否旋转。

  (5)如果pParent的平衡因子为+2 \ -2,则parent的平衡因子违反平衡树的性质,需要对其进行旋转处理。

在这里插入图片描述

在这里插入图片描述

if (parent->_bf == 0)
{
	//更新结束
	break;
}
else if (parent->_bf == 1 || parent->_bf == -1)
{
	//继续往上更新
	cur = parent;
	parent = parent->_parent;
}
else if (parent->_bf == 2 || parent->_bf == -2)
{
	//子树不平衡了,需要旋转
}
else//如果有其他情况直接报错
{
	assert(false);
}

             

2.2AVL树的旋转

  AVL的旋转分为4种情况:

  (1)左单旋转(Left Single Rotation):当新节点cur插入在较高右子树的右侧时进行左单旋转。具体步骤为将curleft变为parent的右子树,将parent节点变为cur的左子树,然后更新相关节点的指向。如果parent是根节点,那么cur将成为新的根节点。

  (2)右单旋转(Right Single Rotation):当新节点cur插入在较低左子树的左侧时进行右单旋转。具体步骤为将curright变为parent的左子树,将parent节点变为cur的右子树,然后更新相关节点的指向。

  (3)右左双旋转(Right Left Double Rotation):先进行右单旋转,再进行左单旋转。当新节点cur插入在的左子树的右侧时,先进行右单旋转,再进行左单旋转。

  (4)左右双旋转(Left Right Double Rotation):先进行左单旋转,再进行右单旋转。当新节点cur插入在的右子树的左侧时,先进行左单旋转,再进行右单旋转。

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)//右左双旋
	{
		RotateRL(parent);
	}
	else if (parent->_bf == -2 && cur->_bf == 1)//左右双旋
	{
		RotateLR(parent);
	}

	break;
}

             

2.2.1左旋

  (1)左单旋转(Left Single Rotation):当新节点cur插入在较高右子树的右侧时进行左单旋转。具体步骤为将curleft变为parent的右子树,将parent节点变为cur的左子树,然后更新相关节点的指向。如果parent是根节点,那么cur将成为新的根节点。

在这里插入图片描述

//左旋
void RotateL(Node* parent)
{
	//创建cur节点和父节点
	Node* cur = parent->_right;
	Node* curleft = cur->_left;

	//将右子树的左节点连接在parent的右节点上
	parent->_right = curleft;//关键步骤1
	if (curleft)//如果右子树的左节点不为空,连接一下父节点
	{
		curleft->_parent = parent;
	}

	//将父节点断开连接到原来右节点的左子树上,降低二叉树高度
	cur->_left = parent;//关键步骤2

	//仍需要处理特殊情况
	//如果原父节点不为_root,保存父节点的父节点
	Node* ppnode = parent->_parent;
	
	//两个节点连接
	parent->_parent = cur;

	//如果父节点为_root,直接更新
	if (parent == _root)
	{
		_root = cur;
		cur->_parent = nullptr;
	}
	else//如果父节点不为_root,需要重新连接
	{
		if (ppnode->_left == parent)
		{
			ppnode->_left = cur;
		}
		else//判断是父父节点的左节点还是右节点
		{
			ppnode->_right = cur;

		}

		//反转将cur节点连接父节点
		cur->_parent = ppnode;
	}

	//更新平衡因子
	parent->_bf = cur->_bf = 0;
}

             

2.2.2右旋

  (2)右单旋转(Right Single Rotation):当新节点cur插入在较低左子树的左侧时进行右单旋转。具体步骤为将curright变为parent的左子树,将parent节点变为cur的右子树,然后更新相关节点的指向。

在这里插入图片描述

//右旋
	void RotateR(Node* parent)
	{
		//取子节点和子节点中的最大节点,作为父节点的左子树
		Node* cur = parent->_left;
		Node* curright = cur->_right;

		//将父节点和左子树中的最大节点连接,降低层高
		parent->_left = curright;//重要步骤1
		if (curright)
		{
			curright->_parent = parent;
		}

		//将子节点作为根,并将原来父节点连接在子节点的右节点
		cur->_right = parent;//重要步骤2

		//上面的代码基本可以完成右旋操作,但是还要考虑parent是否为_root
		Node* ppnode = parent->_parent;

		parent->_parent = cur;

		if (ppnode == nullptr)//parent为_root
		{
			_root = cur;
			cur->_parent = nullptr;
		}
		else//parent不为_root
		{
			if (ppnode->_left == parent)
			{
				ppnode->_left = cur;
			}
			else//判断cur节点在原来子树的右边还是左边,并且连接
			{
				ppnode->_right = cur;
			}

			cur->_parent = ppnode;
		}

		//右旋完成,更新平衡因子
		parent->_bf = cur->_bf = 0;
	}

             

2.2.3右左双旋

  双旋源码放在全部源码中。

  (3)右左双旋转(Right Left Double Rotation):先进行右单旋转,再进行左单旋转。当新节点cur插入在的左子树的右侧时,先进行右单旋转,再进行左单旋转。

在这里插入图片描述

             

2.2.4左右双旋

  (4)左右双旋转(Left Right Double Rotation):先进行左单旋转,再进行右单旋转。当新节点cur插入在的右子树的左侧时,先进行左单旋转,再进行右单旋转。

在这里插入图片描述

             

全部源码

#pragma once

#include<assert.h>

template<class K, class V>
struct AVLTreeNode
{
	pair<K, V> _kv;
	AVLTreeNode<K, V>* _left;
	AVLTreeNode<K, V>* _right;
	AVLTreeNode<K, V>* _parent;
	int _bf;   //平衡因子

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

template<class K,class V>
class AVLTree
{
	typedef AVLTreeNode<K, V> Node;
public:
	//AVL树插入一个节点
	bool AVLInsert(const pair<K, V>& kv)
	{
		//创建cur指向根节点
		Node* cur = _root;
		Node* parent = nullptr;

		//如果AVL树为空,直接返回创建的新节点
		if (_root == nullptr)
		{
			_root = new Node(kv);
			return true;
		}
		
		//如果AVL树不为空,寻找可以插入的节点
		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;

		//AVL树要保持平衡,控制平衡因子为-1、0、1
		while (parent)
		{
			if (cur == parent->_left)
			{
				parent->_bf--;
			}
			else if (cur == parent->_right)
			{
				parent->_bf++;
			}
			
			if (parent->_bf == 0)
			{
				//更新结束
				break;
			}
			else if (parent->_bf == 1 || parent->_bf == -1)
			{
				//继续往上更新
				cur = parent;
				parent = parent->_parent;
			}
			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)//右左双旋
				{
					RotateRL(parent);
				}
				else if (parent->_bf == -2 && cur->_bf == 1)//左右双旋
				{
					RotateLR(parent);
				}

				break;
			}
			else
			{
				assert(false);
			}
		}

		return true;
	}

	//左旋
	void RotateL(Node* parent)
	{
		//创建cur节点和父节点
		Node* cur = parent->_right;
		Node* curleft = cur->_left;

		//将右子树的左节点连接在parent的右节点上
		parent->_right = curleft;//关键步骤1
		if (curleft)//如果右子树的左节点不为空,连接一下父节点
		{
			curleft->_parent = parent;
		}

		//将父节点断开连接到原来右节点的左子树上,降低二叉树高度
		cur->_left = parent;//关键步骤2

		//仍需要处理特殊情况
		//如果原父节点不为_root,保存父节点的父节点
		Node* ppnode = parent->_parent;
		
		//两个节点连接
		parent->_parent = cur;

		//如果父节点为_root,直接更新
		if (parent == _root)
		{
			_root = cur;
			cur->_parent = nullptr;
		}
		else//如果父节点不为_root,需要重新连接
		{
			if (ppnode->_left == parent)
			{
				ppnode->_left = cur;
			}
			else//判断是父父节点的左节点还是右节点
			{
				ppnode->_right = cur;

			}

			//反转将cur节点连接父节点
			cur->_parent = ppnode;
		}

		//更新平衡因子
		parent->_bf = cur->_bf = 0;
	}

	//右旋
	void RotateR(Node* parent)
	{
		//取子节点和子节点中的最大节点,作为父节点的左子树
		Node* cur = parent->_left;
		Node* curright = cur->_right;

		//将父节点和左子树中的最大节点连接,降低层高
		parent->_left = curright;//重要步骤1
		if (curright)
		{
			curright->_parent = parent;
		}

		//将子节点作为根,并将原来父节点连接在子节点的右节点
		cur->_right = parent;//重要步骤2

		//上面的代码基本可以完成右旋操作,但是还要考虑parent是否为_root
		Node* ppnode = parent->_parent;

		parent->_parent = cur;

		if (ppnode == nullptr)//parent为_root
		{
			_root = cur;
			cur->_parent = nullptr;
		}
		else//parent不为_root
		{
			if (ppnode->_left == parent)
			{
				ppnode->_left = cur;
			}
			else//判断cur节点在原来子树的右边还是左边,并且连接
			{
				ppnode->_right = cur;
			}

			cur->_parent = ppnode;
		}

		//右旋完成,更新平衡因子
		parent->_bf = cur->_bf = 0;
	}

	//右左双旋
	void RotateRL(Node* parent)
	{
		//找到双旋节点
		Node* cur = parent->_right;
		Node* curleft = cur->_left;
		int bf = curleft->_bf;//记录平衡因子

		RotateR(parent->_right);//先右旋cur,让节点保持在一条直线上
		RotateL(parent);//左旋parent

		//不同情况更新不同的平衡因子
		if (bf == 0)//新增的节点就是所需要右左旋的节点
		{
			cur->_bf = 0;
			curleft->_bf = 0;
			parent->_bf = 0;
		}
		else if (bf == 1)//新增节点的父节点平衡因子为1,新增在了左边
		{
			cur->_bf = 0;
			curleft->_bf = 0;
			parent->_bf = -1;
		}
		else if (bf == -1)//新增节点的父节点平衡因子为-1,新增在了右边
		{
			cur->_bf = 1;
			curleft->_bf = 0;
			parent->_bf = 0;
		}
		else
		{
			assert(false);
		}
	}

	//左右双旋
	void RotateLR(Node* parent)
	{
		//找到双旋节点
		Node* cur = parent->_left;
		Node* curright = cur->_right;
		int bf = curright->_bf;

		RotateL(parent->_left);//先右旋
		RotateR(parent);//再左旋

		//更新平衡因子,新增节点更新位置不同,节点的平衡因子也不同
		if (bf == 0)
		{
			cur->_bf = 0;
			curright->_bf = 0;
			parent->_bf = 0;
		}
		else if (bf == 1)
		{
			cur->_bf = -1;
			curright->_bf = 0;
			parent->_bf = 0;
		}
		else if (bf == -1)
		{
			cur->_bf = 0;
			curright->_bf = 0;
			parent->_bf = 1;
		}
	}

	//求AVL树高
	int AVLHeight()
	{
		return _AVLHeight(_root);
	}

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

		int leftHeight = _AVLHeight(root->_left);
		int rightHeight = _AVLHeight(root->_right);

		return leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;
	}

	//判断AVL树是否平衡
	bool AVLIsBalance()
	{
		return _AVLIsBalance(_root);
	}

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

		int leftHeight = _AVLHeight(root->_left);
		int rightHeight = _AVLHeight(root->_right);

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

		return abs(rightHeight = leftHeight) < 2 && _AVLIsBalance(root->_left)&& _AVLIsBalance(root->_right);
	}

private:
	Node* _root = nullptr;
};

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

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

相关文章

jdk 中的 keytool 的使用,以及提取 jks 文件中的公钥和私钥

这里暂时只需要知道如何使用就可以了。 首先是生成一个密钥&#xff0c; keytool -genkeypair -alias fanyfull -keypass ffkp123456 -validity 365 -storepass ffsp123456 -keystore fanyfull.jks -keyalg RSA解释一下这里的选项&#xff0c; -alias 密钥对的名称-keypass …

等变性的AI:从离散到连续

这篇文章主要介绍了在科学问题中如何实现不变性或等变性&#xff0c;其中介绍了实现等变性的数学和物理基础&#xff0c;包括离散和连续对称变换的示例&#xff0c;并描述了在实践中如何使用张量积。文章还讨论了如何处理数据中的对称性&#xff0c;以及如何开发适应对称性约束…

喜报!Coremail荣获广东省信息技术应用创新优秀产品和解决方案

导语 近日&#xff0c;由广东省信息技术应用创新产业联盟组织的“2023 年广东省信息技术应用创新成果交流会”在广州顺利召开。 Coremail作为会员单位受邀出席此次交流会。会上&#xff0c;Coremail荣获优秀产品和解决方案奖项&#xff0c;CACTER邮件安全网关获评“2022年广东…

阿里云服务器操作系统怎么选择?阿里云镜像区别

阿里云服务器操作系统怎么选择&#xff1f;小白镜像选择攻略&#xff01;阿里云服务器镜像怎么选择&#xff1f;云服务器操作系统镜像分为Linux和Windows两大类&#xff0c;Linux可以选择Alibaba Cloud Linux&#xff0c;Windows可以选择Windows Server 2022数据中心版64位中文…

想了解商品期权开户门槛?零门槛开户,不用担心!

商品期权不是零门槛开户&#xff0c;不过可以通过第三方期权分仓软件实现0门槛参与商品期权&#xff0c;股指期权&#xff0c;上证50ETF期权、沪深300ETF期权和创业板ETF期权交易&#xff0c;下文介绍想了解商品期权开户门槛&#xff1f;零门槛开户&#xff0c;不用担心&#x…

【Seata】04 - Seata TCC 模式 Demo 调用流程分析

文章目录 前言参考目录版本说明前置知识1、TCC 模式预留资源2、TCC 模式可能会出现的问题2.1、幂等性问题2.2、空回滚问题2.3、悬挂问题 测试 Demo1、数据库表结构2、模块说明3、调用逻辑说明4、分析流程说明 Seata TCC 模式 Commit 调用流程1、调用流程图2、TCC 动作拦截器&am…

【华为云云耀云服务器L实例评测|使用教学】一文带你快速入手华为云云耀云服务器L实例

&#x1f935;‍♂️ 个人主页: AI_magician &#x1f4e1;主页地址&#xff1a; 作者简介&#xff1a;CSDN内容合伙人&#xff0c;全栈领域优质创作者。 &#x1f468;‍&#x1f4bb;景愿&#xff1a;旨在于能和更多的热爱计算机的伙伴一起成长&#xff01;&#xff01;&…

DC/DC开关电源学习笔记(七)低压大电流DC/DC变换技术

低压大电流DC/DC变换技术 1. 无暂态要求的低压大电流DC/DC变换技术2. 负载极其快速变化的低压大电流DC/DC变换技术2.1 非隔离型 VRM2.2 隔离型VRM低压大电流高功率 DC/DC 变换技术,已从前些年的 3.3V 降至现在的 1.0V 左右,电流目前已可达到几十安至几百安。同时,电源的输出指标…

使用CFimagehost源码搭建免费的PHP图片托管私人图床,无需数据库支持

文章目录 1.前言2. CFImagehost网站搭建2.1 CFImagehost下载和安装2.2 CFImagehost网页测试2.3 cpolar的安装和注册 3.本地网页发布3.1 Cpolar临时数据隧道3.2 Cpolar稳定隧道&#xff08;云端设置&#xff09;3.3.Cpolar稳定隧道&#xff08;本地设置&#xff09; 4.公网访问测…

vue3+scss开启写轮眼

vue3scss开启写轮眼 一、相关技术二、使用步骤1.安装依赖2.眼球3 勾玉4 旋转动画5 综合 一、相关技术 采用vue3vitescss的技术内容进行开发 二、使用步骤 1.安装依赖 代码如下&#xff1a; npm install sass2.眼球 首先我们根据需要 将眼睛的基础形状描绘出来&#xff0c…

【AI+医疗】AI在医疗影像设备工作周期中的应用探索

导读 随着人工智能技术的飞速发展&#xff0c;越来越多的领域开始与人工智能技术深度融合&#xff0c;产生了一种新型的技术模式——AI。AI是指将人工智能技术与其他领域的技术或应用进行结合&#xff0c;在提高效率、精度和创新能力的同时&#xff0c;也为人工智能技术的发展提…

The Sandbox 和 Burrito 钱包达成合作!

我们很高兴宣布与 Burrito 钱包达成合作&#xff0c;这是韩国领先的区块链公司 Rotonda 推出的一款全新的 Web3 钱包。此次合作旨在丰富 The Sandbox 平台的用户体验&#xff0c;实现更无缝的资产管理和转移。 此次合作的主要优势之一是将 Burrito 钱包集成到 The Sandbox 生态…

功率放大器的作用有哪些

功率放大器是电子设备中常见的一个组件&#xff0c;其作用是将输入信号的能量放大到更高的功率级别&#xff0c;以用于驱动高功率负载或者提供足够的功率来满足特定需求。功率放大器在各种应用领域中发挥着重要作用&#xff0c;下面将详细介绍功率放大器的作用与应用。 图&…

【【萌新编写RiscV之经典计算机组成与设计RISCV书记录 总4】】

萌新编写RiscV之经典计算机组成与设计RISCV书记录 总4 其实我一开始不想做这个因为实在是实现的功能复杂 但是都差不多的逻辑 基本目的 实现下列操作 条件分支指令branch if equal (beq)。 算术逻辑指令 add sub and or 条件分支指令branch if equal (beq)。 我们确定一下CP…

最新ChatGPT网站源码+支持GPT4.0+支持Midjourney绘画+支持国内全AI模型

一、智能创作系统 SparkAi创作系统是基于国外很火的ChatGPT进行开发的Ai智能问答系统。本期针对源码系统整体测试下来非常完美&#xff0c;可以说SparkAi是目前国内一款的ChatGPT对接OpenAI软件系统。那么如何搭建部署AI创作ChatGPT&#xff1f;小编这里写一个详细图文教程吧&…

【MySQL从删库到跑路 | 基础第二篇】——谈谈SQL中的DML语句

个人主页&#xff1a;兜里有颗棉花糖 欢迎 点赞&#x1f44d; 收藏✨ 留言✉ 加关注&#x1f493;本文由 兜里有颗棉花糖 原创 收录于专栏【MySQL学习专栏】&#x1f388; 本专栏旨在分享学习MySQL的一点学习心得&#xff0c;欢迎大家在评论区讨论&#x1f48c; 前言 前面我们…

华为Mate 60难以撼动苹果的市场份额

来源&#xff1a;猛兽财经 作者&#xff1a;猛兽财经 总结&#xff1a; &#xff08;1&#xff09;一些分析师认为&#xff0c;如果华为能够大规模生产Mate 60&#xff0c;那么华为的新智能手机将对苹果的市场份额构成威胁。 &#xff08;2&#xff09;还有消息称中国将禁止某…

【Node.js】模块化:

文章目录 1、模块化的基本概念2、Node.js 中模块化【1】Node.js 中模块的分类【2】加载模块【3】模块作用域【4】向外共享模块作用域中的成员【5】模块化规范 3、npm与包&#xff08;包/依赖/插件&#xff09;【1】包的基本知识&#xff1a;【2】开发属于自己的包【3】发布包 4…

在网站标题中使用可以让搜索引擎更容易(识别网站的主要内容)

随着互联网的飞速发展&#xff0c;越来越多的企业开始重视网站的优化。优化网站排名不仅可以增加曝光率和点击率&#xff0c;也可以提高品牌知名度和销售额。本文将从关键字优化方案入手&#xff0c;为大家详细介绍如何提升网站排名。 什么是关键字&#xff1f; 关键字是指用…

ChatGPT 网站合集/NovelAI tag生成器/Novel资源大全

ChatGPT 网站合集 https://github.com/xx025/carrot NovelAI tag生成器 https://wolfchen.top/tag/ Novel资源大全 https://wolfchen.top/tag/doc.html 简单地说&#xff0c;Stable Diffusion被修改后做出了NovelAI&#xff0c;NovelAI离家出走便有了Naifu Naifu简单好上手&am…