c++中的二叉搜索树

news2024/12/23 4:38:34

一·概念:

静图展示:

动图展示:

①左子树不为空,则左子树节点值小于根节点值。

②右子树不为空,则右子树节点值大于根节点值。

③左右子树均为二叉搜索树。

④对于它可以插入相等的也可以插入不相等的,这里如果插入的话一般执行的就是覆盖操作,也就是不允许插入如:

注:以二叉树为底层的容器:map(key_value模型),set(key模型),multimap,multiset,其中前两个不支持插入相等的值,而后两者便支持。

二·性能分析:

最优情况下,⼆叉搜索树为完全⼆叉树(或者接近完全⼆叉树),其⾼度为:O(log2 N)

最差情况下,⼆叉搜索树退化为单⽀树(或者类似单⽀),其⾼度为:O( N)

所以综合⽽⾔⼆叉搜索树增删查改时间复杂度为:O(N)

下面是它的缺点:插入的数据在它中应该是有序的,而且要知道这样会可以随机访问里面的数据,那么插入与删除就变得复杂了,因此引出后面需要的平衡二叉树。

三·实现步骤:

下面把它的主体分为三点:插入,删除(复杂点),查找,(不支持修改,因为会改变这棵树的性质)。

3·1插入:

这里比较简单,就是找比这个节点值大就往右走,小就往左走,直到走到空,就可以开辟节点并插入,但是问题就是连接起来,因此需要保存上一个也就是parent节点:

bool insert(const k& key) {
	//头结点直接加:
	if (_root == nullptr) {
		_root = new bsnode<k>(key);
		return true;
	}
	else {
		//找到这个位置(通过搜索比较)
		bsnode<k>* parent = nullptr;//为了连接:
		bsnode<k>* cur = _root;
		while (cur) {
			if (cur->_key < key) {
				parent = cur;
				cur = cur->_right;
			}
			else if (cur->_key > key) {

				parent = cur;
				cur = cur->_left;
			}
			else return false;
		}


		cur = new bsnode<k>(key);
		//完成连接操作:
		if (parent->_key > key) parent->_left = cur;
		else parent->_right = cur;
		return true;


	}


}

3·2删除:

当然大体框架到了删除这一步一般就是难点了,因为考虑的情况很多,下面我们画图把它要考虑的情况画出:

代码:

bool erase(const k& key) {
	bsnode<k>* parent = nullptr;
	bsnode<k>* cur = _root;
	if (_root == nullptr) return false;
	//找到这个key的位置
	while (cur) {
		if (cur->_key > key) {
			parent = cur;
			cur = cur->_left;

		}
		else if (cur->_key < key) {
			parent = cur;
			cur = cur->_right;
		}
		else {
			//下面就是找到了,开始删除操作:
			//第一种情况:
			if (cur->_left == nullptr && cur->_right == nullptr) {
				//这里由于parent左右指针还和cur连一起,注意置空
				if (parent == nullptr) {
					delete cur;
				}
				else {
					if (parent->_left == cur) parent->_left = nullptr;
					else if (parent->_right == cur) parent->_right = nullptr;
					delete cur;
				      }
				//这里最后发现是多余分出了,可以和后面归为一类,就是要么左空要么右空一类(下面就先不更改了)
			}



			//第二种情况:
			else if (cur->_left == nullptr) {
				if (parent == nullptr) _root = cur->_right;//后面防止parent为空故用else
				else {
					if (parent->_left == cur) parent->_left = cur->_right;//看到要访问parent的左指针,就
					//要考虑parent是否为空
					else if (parent->_right == cur) parent->_right = cur->_right;
				}
				delete cur;

			}
			//第三种情况:
			else if (cur->_right == nullptr) {
				if (parent == nullptr) _root = cur->_left;
				else {
					if (parent->_left == cur) parent->_left = cur->_left;
					else if (parent->_right == cur) parent->_right = cur->_left;
				}
				delete cur;


			}
			第四种情况:(相对复杂因为这里不能直接删,而是找一个正确位置的节点的key与它互换然后再删除那个节点)
			//这里通过证明可以发现替换的那个节点是cur的右节点的最左节点:因为既然是cur的右节点的最左节点一定比①cur的右key小,
			//然而既然能排到cur的右节点的那块最左边的这个节点,则它一定比cur大,②故就推出比cur的左节点key大
			else {
				bsnode<k>* er_right_min_p = cur;//它的父亲节点
				bsnode<k>* er_right_min = cur->_right;//那个被替换要删除的节点

				while (er_right_min->_left) {
					er_right_min_p = er_right_min;
					er_right_min = er_right_min->_left;//找到这个被替代节点
				}

				cur->_key = er_right_min->_key;//完成覆盖
				//下面判断这个节点是父亲左节点还是右节点连接的:
				if (er_right_min_p->_left == er_right_min)
					er_right_min_p->_left = er_right_min->_right;

				else if (er_right_min_p->_right == er_right_min)
					er_right_min_p->_right = er_right_min->_right;//这里有一种情况就是当while没有进去,也就是
				//er_right_min_p 与er_right_min未改变,这是就是父亲的右指针连接

				delete er_right_min;



			}
			return true;
		}






	}
	return false;
}

 

3·3查找:

这里由于不考虑相同值的情况,因此就按照它的性质去查找即可:

bool find(const k& key) {//这里也可搞成返回bsnode*类型方便使用
	bsnode<k>* cur = _root;
	while (cur) {
		if (cur->_key > key) cur = cur->_left;
		else if (cur->_key < key) cur = cur->_right;
		else return true;
	}
	return false;
}

四·应用(key与key_value):

4·1key模型:

上面提到了set容器就是key模型:

简单来说就是key不是决定了树的性质排列,然后值是对key来完成增删查等操作。

场景1:小区车牌号放行系统简单模拟。

场景2:检查单词是否出现拼写错误。

实现代码:

namespace K {
	template< typename k>
	struct bsnode {
		k _key;
		bsnode<k>* _left;
		bsnode<k>* _right;
		bsnode(const k& key) :
			_key(key),
			_left(nullptr),
			_right(nullptr) {}



	};


	template< typename k>
	class bstree {

	public:

		bstree() = default;
		bstree(bstree<k>& t) {
			_root = copy(t._root);//这里利用copy函数搞了个临时对象,为了不让swap操作这个root而是操作的临时对象
		}
		bstree<k> operator=(bstree<k> t) {
			swap(_root, t._root);//t自定义类型会调用拷贝构造然后swap交换的事这个临时对象,t不受影响
			return *this;
		}

		bool insert(const k& key) {
			//头结点直接加:
			if (_root == nullptr) {
				_root = new bsnode<k>(key);
				return true;
			}
			else {
				//找到这个位置(通过搜索比较)
				bsnode<k>* parent = nullptr;//为了连接:
				bsnode<k>* cur = _root;
				while (cur) {
					if (cur->_key < key) {
						parent = cur;
						cur = cur->_right;
					}
					else if (cur->_key > key) {

						parent = cur;
						cur = cur->_left;
					}
					else return false;
				}


				cur = new bsnode<k>(key);
				//完成连接操作:
				if (parent->_key > key) parent->_left = cur;
				else parent->_right = cur;
				return true;


			}


		}


		bool find(const k& key) {//这里也可搞成返回bsnode*类型方便使用
			bsnode<k>* cur = _root;
			while (cur) {
				if (cur->_key > key) cur = cur->_left;
				else if (cur->_key < key) cur = cur->_right;
				else return true;
			}
			return false;
		}

		bool erase(const k& key) {
			bsnode<k>* parent = nullptr;
			bsnode<k>* cur = _root;
			if (_root == nullptr) return false;
			//找到这个key的位置
			while (cur) {
				if (cur->_key > key) {
					parent = cur;
					cur = cur->_left;

				}
				else if (cur->_key < key) {
					parent = cur;
					cur = cur->_right;
				}
				else {
					//下面就是找到了,开始删除操作:
					//第一种情况:
					if (cur->_left == nullptr && cur->_right == nullptr) {
						//这里由于parent左右指针还和cur连一起,注意置空
						if (parent == nullptr) {
							delete cur;
						}
						else {
							if (parent->_left == cur) parent->_left = nullptr;
							else if (parent->_right == cur) parent->_right = nullptr;
							delete cur;
						      }
						//这里最后发现是多余分出了,可以和后面归为一类,就是要么左空要么右空一类(下面就先不更改了)
					}



					//第二种情况:
					else if (cur->_left == nullptr) {
						if (parent == nullptr) _root = cur->_right;//后面防止parent为空故用else
						else {
							if (parent->_left == cur) parent->_left = cur->_right;//看到要访问parent的左指针,就
							//要考虑parent是否为空
							else if (parent->_right == cur) parent->_right = cur->_right;
						}
						delete cur;

					}
					//第三种情况:
					else if (cur->_right == nullptr) {
						if (parent == nullptr) _root = cur->_left;
						else {
							if (parent->_left == cur) parent->_left = cur->_left;
							else if (parent->_right == cur) parent->_right = cur->_left;
						}
						delete cur;


					}
					第四种情况:(相对复杂因为这里不能直接删,而是找一个正确位置的节点的key与它互换然后再删除那个节点)
					//这里通过证明可以发现替换的那个节点是cur的右节点的最左节点:因为既然是cur的右节点的最左节点一定比①cur的右key小,
					//然而既然能排到cur的右节点的那块最左边的这个节点,则它一定比cur大,②故就推出比cur的左节点key大
					else {
						bsnode<k>* er_right_min_p = cur;//它的父亲节点
						bsnode<k>* er_right_min = cur->_right;//那个被替换要删除的节点

						while (er_right_min->_left) {
							er_right_min_p = er_right_min;
							er_right_min = er_right_min->_left;//找到这个被替代节点
						}

						cur->_key = er_right_min->_key;//完成覆盖
						//下面判断这个节点是父亲左节点还是右节点连接的:
						if (er_right_min_p->_left == er_right_min)
							er_right_min_p->_left = er_right_min->_right;

						else if (er_right_min_p->_right == er_right_min)
							er_right_min_p->_right = er_right_min->_right;//这里有一种情况就是当while没有进去,也就是
						//er_right_min_p 与er_right_min未改变,这是就是父亲的右指针连接

						delete er_right_min;



					}
					return true;
				}






			}
			return false;
		}


		~bstree()
		{
			destroy(_root);

		}

		void inorder() {
			_inorder(_root);
			cout << endl;
		}

	private:
		//中序遍历:
		void _inorder(const bsnode<k>* root) {
			if (root == nullptr) return;
			_inorder(root->_left);
			cout << root->_key << " ";
			_inorder(root->_right);


		}
		//后序删除:
		void destroy(bsnode<k>* root) {
			if (root == nullptr) return;
			destroy(root->_left);
			destroy(root->_right);
			delete root;
			root = nullptr;
		}
		//前序安装:
		bsnode<k>* copy(bsnode<k>* root) {
			//这里递归完成:假设copy的递归已经完成到最后一步,即只需要按照根节点把它的左右子树连起来即可
			if (root == nullptr) return nullptr;
			bsnode<k>* newnode = new bsnode<k>(root->_key);
			newnode->_left = copy(root->_left);
			newnode->_right = copy(root->_right);
			return newnode;


		}
		bsnode<k>* _root = nullptr;
	};
}


4·2key_value模型:

key包含了性质,而value不改变性质只是一个标志,可以更改,而查找和删除还是根据key来决定。

场景1:单词的英汉互译。

场景2:无人停车时长收费计算系统模拟。

场景3:一些物品的计数统计。

这里如果要是实现只需要再key模型代码完成一些对value的改变即可:

namespace K_Value {
	template< typename k,typename v>
	struct bsnode {
		k _key;
		v _value;
		bsnode<k,v>* _left;
		bsnode<k,v>* _right;
		bsnode(const k& key, const v& value) :
			_key(key),
			_value(value),
			_left(nullptr),
			_right(nullptr) {}



	};


	template< typename k, typename v>
	class bstree {

	public:

		bstree() = default;
		bstree(bstree<k,v>& t) {
			_root = copy(t._root);//这里利用copy函数搞了个临时对象,为了不让swap操作这个root而是操作的临时对象
		}
		bstree<k,v> operator=(bstree<k,v> t) {
			swap(_root, t._root);//t自定义类型会调用拷贝构造然后swap交换的事这个临时对象,t不受影响
			return *this;
		}

		bool insert(const k& key, const v& value) {
			//头结点直接加:
			if (_root == nullptr) {
				_root = new bsnode<k,v>(key,value);
				return true;
			}
			else {
				//找到这个位置(通过搜索比较)
				bsnode<k,v>* parent = nullptr;//为了连接:
				bsnode<k,v>* cur = _root;
				while (cur) {
					if (cur->_key < key) {
						parent = cur;
						cur = cur->_right;
					}
					else if (cur->_key > key) {

						parent = cur;
						cur = cur->_left;
					}
					else return false;
				}


				cur = new bsnode<k,v>(key,value);
				//完成连接操作:
				if (parent->_key > key) parent->_left = cur;
				else parent->_right = cur;
				return true;


			}


		}


		bsnode<k,v>* find(const k& key) {
			bsnode<k,v>* cur = _root;
			while (cur) {
				if (cur->_key > key) cur = cur->_left;
				else if (cur->_key < key) cur = cur->_right;
				else return cur;
			}
			return nullptr;
		}

		bool erase(const k& key, const v& value) {
			bsnode<k,v>* parent = nullptr;
			bsnode<k,v>* cur = _root;
			if (_root == nullptr) return false;
			//找到这个key的位置
			while (cur) {
				if (cur->_key > key) {
					parent = cur;
					cur = cur->_left;

				}
				else if (cur->_key < key) {
					parent = cur;
					cur = cur->_right;
				}
				else {
					//下面就是找到了,开始删除操作:
					//第一种情况:
					if (cur->_left == nullptr && cur->_right == nullptr) {

						if (parent == nullptr) {
							delete cur;
						}
						else {
							if (parent->_left == cur) parent->_left = nullptr;
							else if (parent->_right == cur) parent->_right = nullptr;
							delete cur;
						}
						//这里最后发现是多余分出了,可以和后面归为一类,就是要么左空要么右空一类(下面就先不更改了)
					}



					//第二种情况:
					else if (cur->_left == nullptr) {
						if (parent == nullptr) _root = cur->_right;//后面防止parent为空故用else
						else {
							if (parent->_left == cur) parent->_left = cur->_right;//看到要访问parent的左指针,就
							//要考虑parent是否为空
							else if (parent->_right == cur) parent->_right = cur->_right;
						}
						delete cur;

					}
					//第三种情况:
					else if (cur->_right == nullptr) {
						if (parent == nullptr) _root = cur->_left;
						else {
							if (parent->_left == cur) parent->_left = cur->_left;
							else if (parent->_right == cur) parent->_right = cur->_left;
						}
						delete cur;


					}
					第四种情况:(相对复杂因为这里不能直接删,而是找一个正确位置的节点的key与它互换然后再删除那个节点)
					//这里通过证明可以发现替换的那个节点是cur的右节点的最左节点:因为既然是cur的右节点的最左节点一定比①cur的右key小,
					//然而既然能排到cur的右节点的那块最左边的这个节点,则它一定比cur大,②故就推出比cur的左节点key大
					else {
						bsnode<k,v>* er_right_min_p = cur;//它的父亲节点
						bsnode<k,v>* er_right_min = cur->_right;//那个被替换要删除的节点

						while (er_right_min->_left) {
							er_right_min_p = er_right_min;
							er_right_min = er_right_min->_left;//找到这个被替代节点
						}

						cur->_key = er_right_min->_key;//完成覆盖
						//下面判断这个节点是父亲左节点还是右节点连接的:
						if (er_right_min_p->_left == er_right_min)
							er_right_min_p->_left = er_right_min->_right;

						else if (er_right_min_p->_right == er_right_min)
							er_right_min_p->_right = er_right_min->_right;//这里有一种情况就是当while没有进去,也就是
						//er_right_min_p 与er_right_min未改变,这是就是父亲的右指针连接

						delete er_right_min;



					}
					return true;
				}






			}
			return false;
		}


		~bstree()
		{
			destroy(_root);

		}

		void inorder() {
			_inorder(_root);
			cout << endl;
		}

	private:
		//中序遍历:
		void _inorder(const bsnode<k,v>* root) {
			if (root == nullptr) return;
			_inorder(root->_left);
			cout << root->_key << ":"<<root->_value;
			_inorder(root->_right);


		}
		//后序删除:
		void destroy(bsnode<k,v>* root) {
			if (root == nullptr) return;
			destroy(root->_left);
			destroy(root->_right);
			delete root;
			root = nullptr;
		}
		//前序安装:
		bsnode<k,v>* copy(bsnode<k,v>* root) {
			//这里递归完成:假设copy的递归已经完成到最后一步,即只需要按照根节点把它的左右子树连起来即可
			if (root == nullptr) return nullptr;
			bsnode<k>* newnode = new bsnode<k,v>(root->_key);
			newnode->_left = copy(root->_left);
			newnode->_right = copy(root->_right);
			return newnode;


		}
		bsnode<k,v>* _root = nullptr;
	};

}

到此为止希望对你对二叉搜索树的理解有点帮助,感谢支持!!! 

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

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

相关文章

MATLAB系列02:MATLAB基础

MATLAB系列02&#xff1a;MATLAB基础 2. MATLAB基础2.1 变量和数组2.2 MATLAB变量的初始化2.2.1 用赋值语句初始化变量2.2.2 用捷径表达式赋值2.2.3 使用内置函数来初始化2.2.4 使用关键字input来初始化 2.3 多维数组2.3.1 创建多维数组2.3.2 多维数组在内存中的存储2.3.3 用单…

深入理解FastAPI中的root_path:提升API部署灵活性的关键配置

在Web开发领域&#xff0c;FastAPI因其高性能、易于使用和类型提示功能而备受开发者喜爱。然而&#xff0c;当涉及到在生产环境中部署FastAPI应用程序时&#xff0c;我们常常需要面对一些挑战&#xff0c;比如如何正确处理代理服务器添加的路径前缀。这时&#xff0c;root_path…

关于java同步调用多个接口并返回数据

在现代软件开发中&#xff0c;应用程序经常需要与多个远程API接口进行交互以获取数据。Java作为一种流行的编程语言&#xff0c;提供了多种方式来实现这一需求。本文将探讨如何在Java中同步调用多个API接口&#xff0c;并有效地处理和返回数据。 同步调用的必要性 在某些场景下…

vue table id一样的列合并

合并场景&#xff1a;如果id一样&#xff0c;则主表列合并&#xff0c;子表列不做合并&#xff0c;可实现单行、多行合并&#xff0c;亲测&#xff01;&#xff01;&#xff01; 展示效果如图示&#xff1a; 组件代码&#xff1a; // table组件 :span-method"objectSpa…

网络安全 DVWA通关指南 DVWA SQL Injection (Blind SQL盲注)

DVWA SQL Injection (Blind) 文章目录 DVWA SQL Injection (Blind)Low布尔盲注时间盲注sqlmap MediumHighImpossible 参考文献 WEB 安全靶场通关指南 Low 0、分析网页源代码 <?phpif( isset( $_GET[ Submit ] ) ) {// Get input$id $_GET[ id ];// Check database$geti…

基于spring boot的车辆故障综合服务平台设计与实现----附源码 73314

摘 要 近年来&#xff0c;随着社会科技的不断发展&#xff0c;人们的生活方方面面进入了信息化时代。计算机的普及&#xff0c;使得我们的生活更加丰富多彩。本论文基于Spring Boot框架&#xff0c;设计并实现了一个车辆故障综合服务平台&#xff0c;旨在提供便捷、高效的汽车…

c++类模板为什么不能编译到动态库中来使用

在使用c的时候&#xff0c;我们习惯于将类的定义声明在头文件中&#xff0c;即.h文件&#xff1b;将类函数的实现定义在源文件中&#xff0c;即.cpp文件。如果我们要提供的是一个动态库&#xff0c;那么这种方式更常用&#xff0c;使用动态库的时候&#xff0c;包含头文件&…

如何注册Liberty大学并获取Perplexity Pro

俗称白嫖 Perplexity Pro 会员 如何注册Liberty大学并获取Perplexity Pro 1. 访问官网 首先&#xff0c;进入Liberty大学官网 https://www.liberty.edu&#xff0c;点击“Apply”按钮。 2. 选择课程 选择“Online”课程&#xff0c;选择“Certificate”&#xff0c;然后随便…

深入理解Docke工作原理:UnionFS文件系统详解

在容器技术的世界中&#xff0c;文件系统的设计和实现是其关键组成部分&#xff0c;影响着镜像的构建效率、容器的启动速度以及资源的利用率。**UnionFS&#xff08;联合文件系统&#xff09;**作为Docker的核心文件系统技术&#xff0c;通过其独特的分层结构和写时复制&#x…

5 - ZYNQ SDK学习记录(2)

文章目录 1 Vivado工程基本设计2 Vivado工程位置不变2.1 修改设计1 - 增加PS侧QSPI外设2.2 修改设计2 - 增加PL侧AXI GPIO外设2.3 总结 3 Vivado工程位置变动3.1 先修改BD后打开SDK3.2 先打开SDK后修改BD3.3 总结 1 Vivado工程基本设计 Step 1&#xff1a; Vivado版本Vivado …

【观影聊数学】聊聊电影《孤注一掷》中的数学逻辑

反电诈题材影片《孤注一掷》取材于真实案例&#xff0c;揭秘了境外电信网络诈骗黑色产业链的骇人内幕。境外诈骗集团往往以高薪招聘为诱饵&#xff0c;吸引有发财梦的人去境外淘金&#xff0c;一旦人们走出国门&#xff0c;跳入犯罪分子设下的陷阱里&#xff0c;等待他们的将是…

【python爬虫】之scrapy框架介绍

一.什么是Scrapy&#xff1f; Scrapy是一个为了爬取网站数据&#xff0c;提取结构性数据而编写的应用框架&#xff0c;非常出名&#xff0c;非常强悍。所谓的框架就是一个已经被集成了各种功能&#xff08;高性能异步下载&#xff0c;队列&#xff0c;分布式&#xff0c;解析&a…

SpringBoot开发——使用@Slf4j注解实现日志输出

文章目录 1、Lombok简介2、SLF4J简介3、实现步骤3.1 创建SpringBoot项目3.2 添加依赖3.3 使用 Slf4j 注解3.4 输出日志信息 4、结论 在现代Java开发中&#xff0c;日志记录是至关重要的。它不仅帮助开发者调试代码&#xff0c;还便于监控系统运行状态和性能。 Lombok 和 SLF4J …

了解水凝胶纤维制造?自润滑纺丝来帮忙!高韧性纤维用途广!

大家好&#xff0c;今天我们来了解一篇水凝胶纤维文章——《Continuous Spinning of High‐Tough Hydrogel Fibers for Flexible Electronics by Using Regional Heterogeneous Polymerization》发表于《Advanced Science》。在柔性电子领域&#xff0c;水凝胶纤维因其独特的性…

检查一个复数C的实部a和虚部b是否都是有限数值即a和b都不是无限数值、空值cmath.isfinite(x)

【小白从小学Python、C、Java】 【考研初试复试毕业设计】 【Python基础AI数据分析】 检查一个复数C的实部a和虚部b 是否都是有限数值 即a和b都不是无限数值、空值 cmath.isfinite(x) [太阳]选择题 根据给定的Python代码&#xff0c;哪个选项是错误的&#xff1f; import cma…

适合小客厅使用的投影仪推荐:2024年当贝X5S小户型客厅的最佳选择

我们在买投影前都会先看看家里的环境、预算以及自己的需求去选择适合自己的家的那款&#xff1b;正好最近有朋友向我资讯&#xff1a;我家客厅面积不大&#xff0c;有没有适合的家用投影仪推荐啊&#xff1f;对于这种家庭使用环境不大的小客厅我们该如何挑选投影仪&#xff1f;…

人员抽烟AI检测算法在智慧安防领域的创新应用,助力监控智能化

随着人工智能技术的飞速发展&#xff0c;计算机视觉和深度学习算法在各个领域的应用日益广泛。其中&#xff0c;人员抽烟AI检测算法以其高效、精准的特点&#xff0c;成为公共场所、工厂、学校等场景中的得力助手。本文将介绍TSINGSEE青犀AI智能分析网关V4人员抽烟检测算法的基…

JavaScript高级——执行上下文栈

1、在全局代码执行前&#xff0c;JS引擎就会创建一个栈来存储管理所有的执行上下文对象 2、在全局执行上下文&#xff08;window&#xff09;确定后&#xff0c;将其添加到栈中&#xff08;压栈&#xff09; 3、在函数执行上下文创建后&#xff0c;将其添加到栈中&#xff08…

AI 驱动腾讯游戏智能 NPC,开启新纪元

AI 驱动腾讯游戏智能 NPC&#xff0c;开启新纪元 前言AI 驱动腾讯智能 NPC 前言 曾经&#xff0c;游戏 NPC 往往只是按照预设脚本进行简单互动&#xff0c;缺乏深度和灵活性。然而&#xff0c;如今在 AI 的赋能下&#xff0c;NPC 开始展现出前所未有的智能与活力。它们能够进行…

【springboot】父子工程项目搭建

父工程创建 1.新建一个spring项目 2.选择合适的springboot版本&#xff0c;点击【完成】&#xff0c;即创建父工程完毕 3.删除父工程中无用文件&#xff1a;src 创建子工程模块 1.右键项目名->新建&#xff08;news&#xff09;->模块&#xff08;Module&#xff09;…