C++:二叉搜索树模拟实现(KV模型)

news2024/11/14 0:54:17

C++:二叉搜索树模拟实现(KV模型)

  • 前言
  • 模拟实现KV模型
  • 1. 节点封装
  • 2、前置工作(默认构造、拷贝构造、赋值重载、析构函数等)
  • 2. 数据插入(递归和非递归版本)
  • 3、数据删除(递归和非递归版本)
    • 3.1 查找待删除节点位置
    • 3.2 删除数据及相关节点调整
    • 3.3 完整代码以及递归和非递归版本
  • 四、查找数据
  • 五、中序遍历
  • 六、所有代码

前言

 二叉搜索树又称二叉排序树,他对数据有严格的要求,具体表现在以下几个方面:

  1. 如果一个根节点的左子树不为空,则左子树中所有节点的值都必须小于根节点的值;如果它的右子树不为空,则右子树中所有节点的值都必须大于根节点的值。
  2. 它的左右子树也都必须是一个二叉搜索树,也都必须满足第一条。
  3. 二叉搜索树中的每个节点都是唯一的,不允许重复!!!
    在这里插入图片描述

 二叉搜索树的实际应用主要分为K模型和KV模型。

  1. K模型即Key作为关键码,二叉搜索树中只存储Key一个数据。而关键码则是待搜索的值。比如:我们经常通过软件查找是否存在某个单词,是否拼写正确。
  2. KV模型存储的数据中,每个Key对应一个Value,即键值对<Key, Value>。 我们经常通过Key去查找对应的Val.比如:我们通过英文来查找对应的中文,就是一个最常见的KV场景。

模拟实现KV模型

1. 节点封装

由于是KV模型,我们需要存储Key和Value俩个值。同时二叉搜索树也是二叉树,我们需要它的左右节点。因此节点疯转如下:

template<class K, class V>
struct BSTreeNode
{
	K _key;
	V _value;
	BSTreeNode<K, V>* _left;
	BSTreeNode<K, V>* _right;

	//默认构造函数, 用于后续new创建节点
	BSTreeNode(const K& key, const V& value)
		:_key(key)
		, _value(value)
		, _right(nullptr)
		, _left(nullptr)
	{}
};

2、前置工作(默认构造、拷贝构造、赋值重载、析构函数等)

接下来是KV模型封装的框架,以及默认构造、拷贝构造、赋值重载、析构函数。比较简单,就直接给出代码了哈。

template<class K, class V>
	class BSTree
	{
		typedef BSTreeNode<K, V> Node;//节点重命名
	public:
		//默认构造
		BSTree()
			:_root(nullptr)
		{}

		//拷贝构造
		BSTree(BSTree<K, V>& t)
		{
			_root = Copy(t._root);
		}

		//赋值重载
		BSTree<K, V>& operator=(BSTree<K, V> t)
		{
			swap(_root, t._root);
			return *this;
		}

		//析构函数
		~BSTree()
		{
			Destory(_root);
		}
	private:
		Node* _root = nullptr;
	};
}

2. 数据插入(递归和非递归版本)

首先我们需要查找数据待插入的位置(为了保证插入数据后整体依然是一颗二叉搜索树).。同时查找插入位置时,只有key是有严格要求的,Value只是附带。
即:如果根节点为空,即是待插入数据位置;否则开始查找,如果待插入数据大于根节点往右子树节点走;如果待插入数据小于根节点往左子树节点走。不断循环,直到查找到空节点时,即为数据待插入的位置;如果查找到的大小和待插入数据值相等则返回false(确保二叉搜索树中的每个节点唯一)

【非递归版本】:

bool Insert(const K& key, const V& value)
{
	if (_root == nullptr)//根节点为空
	{
		_root = new Node(key, value);
		return true;
	}

	Node* cur = _root;
	Node* parent = nullptr;//后续插入数据链接时,需要和父节点相连
	while (cur)
	{
		if (cur->_key > key)//待插入数据小于当前节点,往左子树查找
		{
			parent = cur;
			cur = cur->_left;
		}
		else if(cur->_key < key)//待插入数据大于当前节点,往右子树查找
		{
			parent = cur;
			cur = cur->_right;
		}
		else//待插入数据等于当前节点,不允许插入
		{
			return false;
		}
	}

	//链接
	Node* newNode = new Node(key, value); 
	//链接时,我们无法确定插入节点时在父节点的左边还是右边,需要进一步比较
	if (parent->_key > key)
		parent->_left = newNode;
	else
		parent->_right = newNode;
	return true;
}

【递归版本】:

bool InsertR(const K& key, const V& value)
{
	//由于我们查找位置需要从根节点开始查找,所以这里通过另一个函数来传递实现
	return _InsertR(_root, key, value);
}
	
bool _InsertR(Node*& root, const K& key, const V& value)
{
	if (root == nullptr)
	{
		//注意上述我们形参都是引用,所以不用新增Parent节点
		root = new Node(key, value);
		return true;
	}

	if (root->_key > key)//待插入数据小于当前节点,往左子树查找
		return _InsertR(root->_left, key, value);
	else if (root->_key < key)//待插入数据大于当前节点,往右子树查找
		return _InsertR(root->_right, key, value);
	else
		return false;
}

3、数据删除(递归和非递归版本)

3.1 查找待删除节点位置

删除数据,我们首先需要和插入数据一样,先查找到待删除节点。和插入类似就不多说了。

【查找待删除数据】:

bool Erase(const K& key)
{
	if (_root == nullptr)//为空即不存在待删除数据
		return false;

	Node* cur = _root;
	Node* parent = nullptr;
	while (cur)
	{
		if (cur->_key > key)//待删除数据小于当前节点,往左子树查找
		{
			parent = cur;
			cur = cur->_left;
		}
		else if (cur->_key < key)//待删除数据大于当前节点,往右子树查找
		{
			parent = cur;
			cur = cur->_right;
		}
		else
		{
			//当前位置即为待删除节点,装备删除数据	
			
		}
	}
	return false;//整棵树中不存在待删除数据
}

3.2 删除数据及相关节点调整

插找到待删除数据后,显然如果只是简单将该节点删除,有可能将不满足二叉搜索树的要求,那怎么办呢?
删除数据分为以下三种情况:

  1. 左子树为空

左子树为空主要分为以下情形:右子树为空,左子树不为空;左右子树均为空(省略)。
在这里插入图片描述
 不管上述那种情况,我们发现只需将父节点的下一个节点指向待删除节点的右指针即可。但需要注意的是,如果待删除节点为根节点,它将没有父节点,需要单独处理。

【代码实现】:

if (cur->_left == nullptr)//左子树为空
{
	if (parent == _root)//cur为根节点
	{
		_root = cur->_right;
	}
	else
	{
		if (parent->_key > cur->_key)//待删除节点在父节点左子树中
		{
			parent->_left = cur->_right;
		}
		else//待删除节点在父节点右子树中
		{
			parent->_right = cur->_right;
		}
	}
	delete cur;
}
  1. 右子树为空

右子树为空分为单纯右子树为空和左右子树均为空(省)。具体处理方式和左子树为空类似就不多说了。
在这里插入图片描述
【代码实现】:

//左右子树均不为空,查找右子树最小元素进行交换后删除
if (parent == _root)//cur为根节点
{
		_root = cur->_left;
	}
	else
	{
		if (parent->_key > cur->_key)
		{
			parent->_left = cur->_left;
		}
		else
		{
			parent->_right = cur->_left;
		}
	}
	delete cur;
}
  1. 左右子树均不为空

这种情况我们可以查找左子树最大值或右子树最小值和待删除删除节点进行交换,交换后我们可以转化为上述两种子问题来删除数据。(接下来博主以交换右子树最小值为例)
在这里插入图片描述

Node* subLeft = cur->_right;
Node* parent = cur;
while (subLeft->_left)
{
	parent = cur;
	subLeft = subLeft->_left;
}
//交换
swap(cur->_key, subLeft->_key);
swap(cur->_value, subLeft->_value);
//删除
if (parent->_right = subLeft)
{
	parent->_right = subLeft->_right;
}
else
{
	parent->_left = subLeft->_right;
}
delete subLeft;

3.3 完整代码以及递归和非递归版本

递归思路和非递归差球不多,就不一一分析了,下面直接给出两种实现方式代码。

【非递归版本】:

bool Erase(const K& key)
{
	if (_root == nullptr)
		return false;

	Node* cur = _root;
	Node* parent = nullptr;
	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)//左子树为空
			{
				if (parent == _root)//cur为根节点
				{
					_root = cur->_right;
				}
				else
				{
					if (parent->_key > cur->_key)
					{
						parent->_left = cur->_right;
					}
					else
					{
						parent->_right = cur->_right;
					}
				}
				delete cur;
			}
			else if (cur->_right == nullptr)//右子树为空
			{
				if (parent == _root)//cur为根节点
				{
					_root = cur->_left;
				}
				else
				{
					if (parent->_key > cur->_key)
					{
						parent->_left = cur->_left;
					}
					else
					{
						parent->_right = cur->_left;
					}
				}
				delete cur;
			}
			else
			{//左右子树均不为空,查找右子树最小元素进行交换后删除
				Node* subLeft = cur->_right;
				Node* parent = cur;
				while (subLeft->_left)
				{
					parent = cur;
					subLeft = subLeft->_left;
				}
				//交换
				swap(cur->_key, subLeft->_key);
				swap(cur->_value, subLeft->_value);
				//删除
				if (parent->_right = subLeft)
				{
					parent->_right = subLeft->_right;
				}
				else
				{
					parent->_left = subLeft->_right;
				}
				delete subLeft;
			}
			return true;
		}
	}
	return false;
}

【递归版本】:

//删除:递归版本
bool EraseR(const K& key)
{
	return _EraseR(_root, key);//同理,由于需要根节点,在通过一层函数来实现
}
bool _EraseR(Node*& root, const K& key)
{
	if (root == nullptr)//非找到
		return false;
	if (root->_key > key)//转化成递归子问题,在左子树中删除key
		return _EraseR(root->_left, key);
	else if (root->_key < key)//转化成递归子问题,在右子树中删除key
		return _EraseR(root->_right, key);
	else
	{//删除数据
		if (root->_left == nullptr)
		{
			Node* del = root;
			root = root->_right;
			delete del;
			return true;
		}
		else if (_root->_right == nullptr)
		{
			Node* del = root;
			root = root->_left;
			delete del;
			return true;
		}
		else
		{
			Node* subLeft = root->_right;
			while (subLeft->_left)
			{
				subLeft = subLeft->_left;
			}
			//交换
			swap(root->_key, subLeft->_key);
			swap(root->_value, subLeft->_value);

			return _EraseR(root->_right, key); 
		}

	}
}

四、查找数据

【递归版本】:

//查找:递归版本
Node* FindR(const K& key)
{
	return _FindR(_root, key);
}
Node* _FindR(Node*& root, const K& key)
{
	if (root == nullptr)
		return nullptr;
	if (root->_key > key)
		return _FindR(root->_left, key);
	else if (root->_key < key)
		return _FindR(root->_right, key);
	else
		return root;
}

【非递归版本】:

//查找:非递归版本
Node* Find(const K& key)
{
	Node* cur = _root;
	while (cur)
	{
		if (cur->_key > key)
			cur = cur->_left;
		else if (cur->_key < key)
			cur = cur->_right;
		else
		{//找到了
			return cur;
		}
	}
	return nullptr;
}

五、中序遍历

//中序遍历
void Inorder()
{
	_Inorder(_root);
	cout << endl;
}

void _Inorder(Node* root)
{
	if (root == nullptr)
		return;
	_Inorder(root->_left);
	cout << root->_key << "->" << root->_value << " " << endl;
	_Inorder(root->_right);
}

六、所有代码

gitee:所有代码及测试代码

namespace KeyValue
{
	template<class K, class V>
	struct BSTreeNode
	{
		K _key;
		V _value;
		BSTreeNode<K, V>* _left;
		BSTreeNode<K, V>* _right;

		//默认构造函数
		BSTreeNode(const K& key, const V& value)
			:_key(key)
			, _value(value)
			, _right(nullptr)
			, _left(nullptr)
		{}
	};

	template<class K, class V>
	class BSTree
	{
		typedef BSTreeNode<K, V> Node;
	public:
//
		//默认构造
		BSTree()
			:_root(nullptr)
		{}

		//拷贝构造
		BSTree(BSTree<K, V>& t)
		{
			_root = Copy(t._root);
		}

		//赋值重载
		BSTree<K, V>& operator=(BSTree<K, V> t)
		{
			swap(_root, t._root);
			return *this;
		}

		//析构函数
		~BSTree()
		{
			Destory(_root);
		}
//
		//插入, 非递归版本
		bool Insert(const K& key, const V& value)
		{
			if (_root == nullptr)
			{
				_root = new Node(key, value);
				return true;
			}

			Node* cur = _root;
			Node* parent = nullptr;
			while (cur)
			{
				if (cur->_key > key)
				{
					parent = cur;
					cur = cur->_left;
				}
				else if(cur->_key < key)
				{
					parent = cur;
					cur = cur->_right;
				}
				else
				{
					return false;
				}
			}

			//链接
			Node* newNode = new Node(key, value); 
			if (parent->_key > key)
				parent->_left = newNode;
			else
				parent->_right = newNode;
			return true;
		}

		// 插入: 递归遍布
		bool InsertR(const K& key, const V& value)
		{
			return _InsertR(_root, key, value);
		}
/
		//查找:非递归版本
		Node* Find(const K& key)
		{
			Node* cur = _root;
			while (cur)
			{
				if (cur->_key > key)
					cur = cur->_left;
				else if (cur->_key < key)
					cur = cur->_right;
				else
				{//找到了
					return cur;
				}
			}
			return nullptr;
		}

		//查找:递归版本
		Node* FindR(const K& key)
		{
			return _FindR(_root, key);
		}
/
		//删除:非递归版本
		bool Erase(const K& key)
		{
			if (_root == nullptr)
				return false;

			Node* cur = _root;
			Node* parent = nullptr;
			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)//左子树为空
					{
						if (parent == _root)//cur为根节点
						{
							_root = cur->_right;
						}
						else
						{
							if (parent->_key > cur->_key)
							{
								parent->_left = cur->_right;
							}
							else
							{
								parent->_right = cur->_right;
							}
						}
						delete cur;
					}
					else if (cur->_right == nullptr)//右子树为空
					{
						if (parent == _root)//cur为根节点
						{
							_root = cur->_left;
						}
						else
						{
							if (parent->_key > cur->_key)
							{
								parent->_left = cur->_left;
							}
							else
							{
								parent->_right = cur->_left;
							}
						}
						delete cur;
					}
					else
					{//左右子树均不为空,查找右子树最小元素进行交换后删除
						Node* subLeft = cur->_right;
						Node* parent = cur;
						while (subLeft->_left)
						{
							parent = cur;
							subLeft = subLeft->_left;
						}
						//交换
						swap(cur->_key, subLeft->_key);
						swap(cur->_value, subLeft->_value);
						//删除
						if (parent->_right = subLeft)
						{
							parent->_right = subLeft->_right;
						}
						else
						{
							parent->_left = subLeft->_right;
						}
						delete subLeft;
					}
					return true;
				}
			}
			return false;
		}

		//删除:递归版本
		bool EraseR(const K& key)
		{
			return _EraseR(_root, key);
		}
/
		//中序遍历
		void Inorder()
		{
			_Inorder(_root);
			cout << endl;
		}
		void _Inorder(Node* root)
		{
			if (root == nullptr)
				return;
			_Inorder(root->_left);
			cout << root->_key << "->" << root->_value << " " << endl;
			_Inorder(root->_right);
		}

	private:
		Node* Copy(Node*& root)
		{
			if (root == nullptr)
				return nullptr;
			Node* newRoot = new Node(root->_key, root->_value);
			newRoot->_left = Copy(root->_left);
			newRoot->_right = Copy(root->_right);
			return newRoot;
		}

		void Destory(Node*& root)
		{
			if (root == nullptr)
				return;
			Destory(root->_right);
			Destory(root->_left);
			delete root;
			root = nullptr;
		}

		bool _EraseR(Node*& root, const K& key)
		{
			if (root == nullptr)
				return false;
			if (root->_key > key)
				return _EraseR(root->_left, key);
			else if (root->_key < key)
				return _EraseR(root->_right, key);
			else
			{//删除数据
				if (root->_left == nullptr)
				{
					Node* del = root;
					root = root->_right;
					delete del;
					return true;
				}
				else if (_root->_right == nullptr)
				{
					Node* del = root;
					root = root->_left;
					delete del;
					return true;
				}
				else
				{
					Node* subLeft = root->_right;
					while (subLeft->_left)
					{
						subLeft = subLeft->_left;
					}
					//交换
					swap(root->_key, subLeft->_key);
					swap(root->_value, subLeft->_value);

					return _EraseR(root->_right, key); 
				}

			}
		}

		bool _InsertR(Node*& root, const K& key, const V& value)
		{
			if (root == nullptr)
			{
				root = new Node(key, value);
				return true;
			}

			if (root->_key > key)
				return _InsertR(root->_left, key, value);
			else if (root->_key < key)
				return _InsertR(root->_right, key, value);
			else
				return false;
		}

		Node* _FindR(Node*& root, const K& key)
		{
			if (root == nullptr)
				return nullptr;
			if (root->_key > key)
				return _FindR(root->_left, key);
			else if (root->_key < key)
				return _FindR(root->_right, key);
			else
				return root;
		}

		Node* _root = nullptr;
	};
}

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

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

相关文章

C# 字体大小的相关问题

设置字体大小无法这么写&#xff0c; button1.Font.Size 20&#xff1b; 这个是只读属性&#xff1b; 把字体大小改为16&#xff0c; button2.Font new Font(button2.Font.Name, 16); 程序运行的时候先看一下窗体和控件的默认字体尺寸&#xff0c;都是9&#xff1b;然后点b…

目标检测 | 卷积神经网络(CNN)详细介绍及其原理详解

前言&#xff1a;Hello大家好&#xff0c;我是小哥谈。卷积神经网络&#xff08;Convolutional Neural Network&#xff0c;CNN&#xff09;是一种深度学习模型&#xff0c;主要用于图像识别和计算机视觉任务。它的设计灵感来自于生物学中视觉皮层的工作原理。CNN的核心思想是通…

RedissonClient妙用-分布式布隆过滤器

目录 布隆过滤器介绍 布隆过滤器的落地应用场景 高并发处理 多个过滤器平滑切换 分析总结 布隆过滤器介绍 布隆过滤器&#xff08;Bloom Filter&#xff09;是1970年由布隆提出的。它实际上是一个很长的二进制向量和一系列随机映射函数。布隆过滤器可以用于检索一个元素是…

Android Studio安装过程遇到SDK无法安装问题解决

首次打开studio遇到该类问题&#xff0c;需要下载SDK文件&#xff0c;后又发现SDK由于是Google源&#xff0c;无法进行正常安装&#xff0c;故转而进行SDK的镜像安装。 一、下载SDK Tools 地址&#xff1a;AndroidDevTools - Android开发工具 Android SDK下载 Android Studio…

华为第二批难题一:基于预训练AI模型的元件库生成

我的理解&#xff1a;华为的这个难道应该是想通过大模型技术&#xff0c;识别元件手册上的图文内容&#xff0c;与现有建库工具结合&#xff0c;有潜力按标准生成各种库模型。 正好&#xff0c;我们正在研究&#xff0c;利用知识图谱技术快速生成装配模型&#xff0c;其中也涉…

微调LLM或使用RAG,开发RAG管道的12个痛点

论文地址&#xff1a;archive.is/bNbZo Pain Point 1: Missing Content 内容缺失 Pain Point 2: Missed the Top Ranked Documents 错过排名靠前的文档 Pain Point 3: Not in Context — Consolidation Strategy Limitations 不在上下文中 — 整合战略的局限性 Pain Point …

DBNet详解及训练ICDAR2015数据集

论文地址&#xff1a;https://arxiv.org/pdf/1911.08947.pdf 开源代码pytorch版本&#xff1a;GitHub - WenmuZhou/DBNet.pytorch: A pytorch re-implementation of Real-time Scene Text Detection with Differentiable Binarization 前言 在这篇论文之前&#xff0c;文字检…

【龙年大礼】| 2023中国开源年度报告!

【中国开源年度报告】由开源社从 2015 年发起&#xff0c;是国内首个结合多个开源社区、高校、媒体、风投、企业与个人&#xff0c;以纯志愿、非营利的理念和开源社区协作的模式&#xff0c;携手共创完成的开源研究报告。后来由于一些因素暂停&#xff0c;在 2018 年重启了这个…

香港倾斜模型3DTiles数据漫游

谷歌地球全香港地区倾斜摄影数据&#xff0c;通过工具转换成3DTiles格式&#xff0c;将这份数据完美加载到三维数字地球Cesium上进行完美呈现&#xff0c;打造香港地区三维倾斜数据覆盖&#xff0c;完美呈现香港城市壮美以及维多利亚港繁荣景象。再由12.5米高分辨率地形数据&am…

Java完整版宿舍管理

项目技术&#xff1a; springboot layui idea mysql5.7 jdk1.8 maven3 有需要该项目的小伙伴可以私信我你的Q。 功能描述&#xff1a; &#xff08;1&#xff09;基本信息管理 基本信息分为学生信息和宿舍信息两部分&#xff0c;其功能是负责维护这些信息&#xff0c…

Ubuntu22.04 gnome-builder gnome C 应用程序习练笔记(二)

gnome-builder创建的程序&#xff0c;在工程树中有三个重要程序&#xff1a;main主程序、application应用程序和window主窗口程序。main整个程序的起始&#xff0c;它会操作application生产应用环境&#xff0c;application会操作window生成主窗口&#xff0c;于是就有了 appli…

【前端模板】bootstrap5披萨餐厅网站Pizza King平台(电商适用,附源码)

一、需求分析 披萨餐厅网站是指由披萨餐厅创建和维护的在线平台&#xff0c;旨在提供与该餐厅相关的信息和服务。以下是一些常见的功能和内容&#xff0c;可以在披萨餐厅网站上找到&#xff1a; 餐厅介绍&#xff1a;网站通常会提供有关餐厅的背景信息&#xff0c;包括其历史、…

VMwawre配置静态ip

1、查看当前虚拟机网关&#xff08;记住这个网关&#xff0c;后面使用&#xff09; 2、进入目录命令&#xff1a;cd /etc/sysconfig/network-scripts/ 3、编辑网卡配置文件命令&#xff1a;vim ifcfg-ens33 4、配置静态IP&#xff0c;修改和增加如下信息&#xff1a; 修改的内…

Vue3编写简单的App组件(二)

一、Vue3页面渲染基本流程 1、入口文件 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><link rel"icon" href"/favicon.ico"><meta name"viewport" content"widthde…

vivo发布2023 年度科技创新;阿里全新AI代理,可模拟人类操作手机

vivo 发布 2023 年度十大产品技术创新 近日&#xff0c;vivo 发布了「2023 年度科技创新」十大产品技术创新榜单&#xff0c;并将这些技术分为了 4 个板块。 「四大蓝科技」为 vivo 在去年推出的全新技术品牌&#xff0c;涵盖蓝晶芯片技术栈、蓝海续航系统、蓝心大模型、蓝河操…

【Linux】Shell编程

Shell编程 目录 Shell编程1.shell基础1.输入重定向 & 输出重定向2.管道3.特殊字符(3.1)通配符(3.2)引号(3.3)注释符(#) 4.别名5.命令历史history 2.Shell脚本Shell脚本的执行方式(1)为脚本文件加上可执行权限,然后在命令行直接输入shell脚本文件名执行。(2)sh shell脚本名(…

redis之布隆过滤

目录 1、redis之布隆过滤 2、布隆过滤器原理 3、布隆过滤器使用步骤 初始化bitmap 添加占坑位 判断是否存在圜 1、redis之布隆过滤 布隆过滤&#xff1a;有一个初值都为0的bit数组和多个哈希函数构成&#xff0c;用来快速判断集合中是否存在某个元素。目的&#xff1a;减…

【深度学习】pytorch 与 PyG 安装(pip安装)

【深度学习】pytorch 与 PyG 安装&#xff08;pip安装&#xff09; 一、PyTorch安装和配置&#xff08;一&#xff09;、安装 CUDA&#xff08;二&#xff09;、安装torch、torchvision、torchaudio三个组件&#xff08;1&#xff09;下载镜像文件&#xff08;2&#xff09;创建…

华为第二批难题五:AI技术提升六面体网格生成自动化问题

有CAE开发商问及OCCT几何内核的网格方面的技术问题。其实&#xff0c;OCCT几何内核的现有网格生成能力比较弱。 HybridOctree_Hex的源代码&#xff0c;还没有仔细去学习。 “HybridOctree_Hex”的开发者说&#xff1a;六面体网格主要是用在数值模拟领域的&#xff0c;比如汽车…

智慧自助餐饮系统(SpringBoot+MP+Vue+微信小程序+JNI+ncnn+YOLOX-Nano)

一、项目简介 本项目是配合智慧自助餐厅下的一套综合系统&#xff0c;该系统分为安卓端、微信小程序用户端以及后台管理系统。安卓端利用图像识别技术进行识别多种不同菜品&#xff0c;识别成功后安卓端显示该订单菜品以及价格并且生成进入小程序的二维码&#xff0c;用户扫描…