二叉搜索数使用,底层原理及代码实现

1:二叉搜索树的定义

二叉搜索树的底层是一个二叉链表

二叉搜索树又称二叉排序树,它或者是一棵空树 ,或者是具有以下性质的二叉树 :
  1. 若它的左子树不为空,则左子树上所有节点的值都小于根节点的值
  2. 若它的右子树不为空,则右子树上所有节点的值都大于根节点的值
  3. 它的左右子树也分别为二叉搜索树

举个例子,我们要把数组[50,87,64,100,24,35,1,77]插入到二叉搜索树中,对应的树应该是这个样子

2.二叉搜索树的特点

  1. 既然以搜索树为名字,显然搜索功能是很强大的(相较于数组的搜索),对于二叉树的每一个节点来说其左子树的任意值绝对小于根节点,右子树的任意值绝对大于根节点,如果我们要查找一个值,该值比根节点小的话去左子树找,比根节点大的话去右子树找,如果二叉搜索树是一颗满二叉树的话,搜索的时间复杂度将为log(N),相当于从全世界70亿人中找一个人仅用了30次操作.
  2. 因为二叉树的左子树的任意值绝对小于根节点,右子树的任意值绝对大于根节点,所以其中序遍历即为升序数组,搜索树理论上不可以同时存在多个相同的元素,因为这是没有意义的,所以严格来说,这是一个无重复元素的升序数组

3.二叉搜索树的底层原理

插入

显然原树在执行完插入操作后仍应该是二叉搜索树,为了不破坏原树的结构,对于根节点来说,如果插入值大于根节点,应该往右插入,插入值小于根节点,应该往左插入.直到最后1.找到的节点为空,代表应该向空节点处插入2.存在节点值 == 插入值,这样的插入是无意义的,插入失败!

以上图为例,如果要插入一个76

模拟实现

/*	template <class T>
	//二叉搜索的节点
	struct TreeNode {
		typedef TreeNode<T> Node;
		Node* _left;
		Node* _right;
		T  _val;
		//构造函数
		TreeNode(T val = T())
			:_left(nullptr),
			_right(nullptr),
			_val(val)
		{}
	};
*/

bool insert(T val) {
	if (_root == nullptr)
	{
		_root = new Node(val);
	}
	Node* child = _root;
	Node* parent = _root;
	while (child != nullptr) {
		parent = child;
		if (val < child->_val)	//val比根节点小,往左走
		{
			child = child->_left;
		}
		else if (val > child->_val)	//val比根节点大,往右走
		{
			child = child->_right;
		}
		else {
			return false;	//val与根节点相等,二叉搜索树中不许存在相同元素,所以不可插入,返回假
		}
	}
	if (parent->_val < val)	
		parent->_right = new Node(val);
	else if (parent->_val > val)	
		parent->_left = new Node(val);
	return true;	//插入成功
}

查找

查找与插入很类似,大于的话去右边找,小于的话去左边找,找到了返回节点,没找到返回nullptr

以找64为例

模拟实现

/*	template <class T>
	//二叉搜索的节点
	struct TreeNode {
		typedef TreeNode<T> Node;
		Node* _left;
		Node* _right;
		T  _val;
		//构造函数
		TreeNode(T val = T())
			:_left(nullptr),
			_right(nullptr),
			_val(val)
		{}
	};
*/

Node* find(T val) {
	Node* tmp = _root;
	while (tmp != nullptr) {
		if (val == tmp->_val)
			return tmp;	//找到啦
		else if (val > tmp->val) {
			tmp = tmp->_right;		//val大于该节点的值,去右子树找
		}
		else {
			tmp = tmp->_left;		//val小于该节点的值,去左子树找
		}
	}
	return nullptr;		//树里没有val,不存在该节点
}

删除

这边我们重画一个比较大一些的搜索二叉树以便于举例,数据很简单,1到16

首先,当删去8时,其余的所有节点都仍旧符合搜索二叉树的定义,删去其他叶子节点有相同的效果,同理,在删去2节点时,理论上来说仅有2的子树受到影响,搜索树的其他部分仍旧符合定义.

再以删去九号节点为例,由于二叉树的定义,删去九号节点后新节点的值必须满足除自身外,左子树都小于其,右子树都大于其.

由上图为例,对二叉树进行分析可得:除8外的左树<8<9<10<除10外的右树,也就是说:

左子树的右边的右边的右边的......的叶节点和右子树左边的左边的.......的叶节点最适合做根节点


特殊情况1:左子树没有右节点

使用左子树自身的根节点即可.


特殊情况2:没有左子树&&只有一个孩子节点

上图为删除节点4的示意图,可以看出,4号节点没有左子树,此时4号节点仅有一个子节点,直接让子节点代替自己即可


模拟实现

		bool earse(const T& val) {
			Node* parent = _root;
			Node* child = _root;
			while (child!= nullptr) {
				if (val > child->_val)
				{
					parent = child;
					child = child->_right;
				}
				else if (val < child->_val){
					parent = child;
					child = child->_left;
				}
				else   //相等
				{
					if (child->_left == nullptr)
					{
						if (child == _root)
							_root = child->_right;
						else {
							if (parent->_left == child)	parent->_left = child->_right;
							else if (parent->_right == child)	parent->_right = child->_right;
						}
						delete child;
					}
					else if (child->_right == nullptr) {
						if (child == _root)
							_root = child->_left;
						else {
							if (parent->_left == child)	parent->_left = child->_left;
							else if (parent->_right == child)	parent->_right = child->_left;
						}
						delete child;
					}
					else {
						Node* tmp = child->_left;
						Node* pp = tmp;

						while (tmp->_right) {
							pp = tmp;
							tmp = tmp->_right;
						}
						child->_val = tmp->_val;

						if (pp != tmp)
							pp->_right = tmp->_left;
						delete tmp;
					}
					return true;
				}
			}
			return false;
		}

高度(不重要)

	public:
		size_t height() {
			return _height(_root);
		}
	private:
		size_t _height(Node* root) {
			if (root == nullptr)
				return 0;
			else
				return _height(root->_left) + _height(root->_right) + 1;
		}

节点个数(不重要)

	public:
		size_t size() {
			return _size(_root);
		}
	private:
		size_t _size(Node* root) {
			if (root == nullptr)
				return 0;
			return _size(root->_left) + _size(root->_right) + 1;
		}

打印(不重要)

void print() {
	std::queue<Node*> q1,q2;
	q1.push(_root);
	while (!q1.empty() || !q2.empty()) {
		while (!q1.empty()) {
			if (q1.front() == nullptr)
				std::cout << '#' << ' ';
			else {
				q2.push(q1.front()->_left);
				q2.push(q1.front()->_right);
				std::cout << q1.front()->_val << ' ';
			}
			q1.pop();
		}
		std::swap(q1,q2);
		std::cout << std::endl;
	}

}

搜索二叉树模拟实现

#include <queue>
#include <iostream>
namespace SearchTree {
	template <class T>
	//二叉搜索的节点
	struct TreeNode {
		typedef TreeNode<T> Node;
		Node* _left;
		Node* _right;
		T  _val;
		//构造函数
		TreeNode(T val = T())
			:_left(nullptr),
			_right(nullptr),
			_val(val)
		{}
	};
	template <class T>
	class SearchTree {
		typedef TreeNode<T> Node;
		Node* _root;	//根节点
	public:
		SearchTree():_root(nullptr){}		//构造函数
		bool insert(T val) {
			if (_root == nullptr)
			{
				_root = new Node(val);
			}
			Node* child = _root;
			Node* parent = _root;
			while (child != nullptr) {
				parent = child;
				if (val < child->_val)	//val比根节点小,往左走
				{
					child = child->_left;
				}
				else if (val > child->_val)	//val比根节点大,往右走
				{
					child = child->_right;
				}
				else {
					return false;	//val与根节点相等,二叉搜索树中不许存在相同元素,所以不可插入,返回假
				}
			}
			if (parent->_val < val)	
				parent->_right = new Node(val);
			else if (parent->_val > val)	
				parent->_left = new Node(val);
			return true;	//插入成功
		}
		bool earse(const T& val) {
			Node* parent = _root;
			Node* child = _root;
			while (child!= nullptr) {
				if (val > child->_val)
				{
					parent = child;
					child = child->_right;
				}
				else if (val < child->_val){
					parent = child;
					child = child->_left;
				}
				else   //相等
				{
					if (child->_left == nullptr)
					{
						if (child == _root)
							_root = child->_right;
						else {
							if (parent->_left == child)	parent->_left = child->_right;
							else if (parent->_right == child)	parent->_right = child->_right;
						}
						delete child;
					}
					else if (child->_right == nullptr) {
						if (child == _root)
							_root = child->_left;
						else {
							if (parent->_left == child)	parent->_left = child->_left;
							else if (parent->_right == child)	parent->_right = child->_left;
						}
						delete child;
					}
					else {
						Node* tmp = child->_left;
						Node* pp = tmp;

						while (tmp->_right) {
							pp = tmp;
							tmp = tmp->_right;
						}
						child->_val = tmp->_val;

						if (pp != tmp)
							pp->_right = tmp->_left;
						delete tmp;
					}
					return true;
				}
			}
			return false;
		}
		Node* find(T val) {
			Node* tmp = _root;
			while (tmp != nullptr) {
				if (val == tmp->_val)
					return tmp;	//找到啦
				else if (val > tmp->val) {
					tmp = tmp->_right;		//val大于该节点的值,去右子树找
				}
				else {
					tmp = tmp->_left;		//val小于该节点的值,去左子树找
				}
			}
			return nullptr;		//树里没有val,不存在该节点
		}
		void print() {
			std::queue<Node*> q1,q2;
			q1.push(_root);
			while (!q1.empty() || !q2.empty()) {
				while (!q1.empty()) {
					if (q1.front() == nullptr)
						std::cout << '#' << ' ';
					else {
						q2.push(q1.front()->_left);
						q2.push(q1.front()->_right);
						std::cout << q1.front()->_val << ' ';
					}
					q1.pop();
				}
				std::swap(q1,q2);
				std::cout << std::endl;
			}

		}
		size_t size() {
			return _size(_root);
		}
		size_t height() {
			return _height(_root);
		}
	private:
		size_t _size(Node* root) {
			if (root == nullptr)
				return 0;
			return _size(root->_left) + _size(root->_right) + 1;
		}
		size_t _height(Node* root) {
			if (root == nullptr)
				return 0;
			else
				return std::max(_height(root->_left) , _height(root->_right)) + 1;
		}
	};
}

对搜索二叉树的分析及AVL树红黑树的优势

参考我在前文查找处的分析可得,查找的时间复杂度与树的高度,空间复杂度恒为o(1),当插入从a到b的值是,优先输入[a,b]中间的值,后输入接近a,b的极端值,此时树较为平衡,查找的时间复杂度接近o(logn),而当数据有序的进行插入时,树相当于一个链表,时间复杂度较高,为(o(n))

举个例子,当把数据[1,2,3,4,5,6,7]插入二叉树时

以[4,2,1,3,6,5,7]插入时,如果我们要查找7,需要进行三次判断即可,

以[1,2,3,4,5,6,7]插入时,如果我们要查找7,需要进行七次判断!!!!时间复杂度为o(n),再加上额外的空间开销,还不如直接在原数组中查找

如果有一种改进的插入方式可以在插入时,依据原树的高度差进行动态的重构树结构,便可大大加快查找速度

附:AVL树模拟实现(供参考)

#include <queue>
#include <iostream>
#include <assert.h>
namespace AVL {

	template<class T>
	struct AVLTreeNode
	{
		AVLTreeNode(const T& data = T())
			: _pLeft(nullptr)
			, _pRight(nullptr)
			, _pParent(nullptr)
			, _data(data)
			, _bf(0)
		{}

		AVLTreeNode<T>* _pLeft;
		AVLTreeNode<T>* _pRight;
		AVLTreeNode<T>* _pParent;
		T _data;
		int _bf;   // 节点的平衡因子
	};


	// AVL: 二叉搜索树 + 平衡因子的限制
	template<class T>
	class AVLTree
	{
		typedef AVLTreeNode<T> Node;
	public:
		AVLTree()
			: _pRoot(nullptr)
		{}
		void print() {
			std::queue<Node*>q1;
			std::queue<Node*>q2;
			q1.push(_pRoot);
			while (!q1.empty() || !q2.empty()) {
				while (!q1.empty())
				{
					if (q1.front() != nullptr) {
						std::cout << q1.front()->_data << ' ';
						q2.push(q1.front()->_pLeft);
						q2.push(q1.front()->_pRight);
					}
					else
						std::cout << '#' << ' ';
					q1.pop();
				}
				swap(q1, q2);
				std::cout << std::endl;
			}
		}
		// 在AVL树中插入值为data的节点
		bool Insert(const T& data) {
			Node* node = new Node(data);
			if (_pRoot == nullptr) {
				_pRoot = node;
				return true;
			}
			Node* parent = _pRoot,*child = _pRoot;
			while (child) {
				parent = child;
				if (child->_data > data)	child = child->_pLeft;
				else if (child->_data < data)	child = child->_pRight;
				else return false;
			}
			if (parent->_data < data)
			{
				parent->_pRight = node;
			}
			else
			{
				parent->_pLeft = node;
			}
			node->_pParent = parent;
			while (parent) {
				if (node == parent->_pLeft)
					parent->_bf--;
				else
					parent->_bf++;
				if (parent->_bf == 0)
					break;
				else if (parent->_bf == 1 || parent->_bf == -1)
					node = parent, parent = parent->_pParent;
				else {		//出问题了
					if (parent->_bf == -2 && node->_bf == -1) {
						RotateR(node);
					}
					else if (parent->_bf == -2 && node->_bf == 1)
					{
						RotateLR(node);
					}
					else if (parent->_bf == 2 && node->_bf == 1) {
						RotateL(parent);
					}
					else if (parent->_bf == -2 && node->_bf == -1) {
						RotateRL(node);
					}
					else {
						//   树损坏
					}
				}
			}
			return true;
		}

		// AVL树的验证
		bool IsAVLTree()
		{
			return _IsAVLTree(_pRoot);
		}

	private:
		// 根据AVL树的概念验证pRoot是否为有效的AVL树
		bool _IsAVLTree(Node* pRoot) {
			std::queue<Node*> q;
			q.push(pRoot);
			while (!q.empty()) {
				if (q.front() == nullptr)
				{
					q.pop();
					continue;
				}
				if (q.front()->_bf >= 2 || q.front()->_bf <= -2)
				{
					return false;
				}
				else {
					q.push(q.front()->_pLeft);
					q.push(q.front()->_pRight);
					q.pop();
				}
			}
			return true;
		}
		size_t _Height(Node* pRoot) {
			size_t h = 0;
			std::queue<Node*> q1;
			std::queue<Node*> q2;
			q2.push(pRoot);
			while (!q1.empty()&&!q2.empty()) {
				while (!q1.empty())
				{
					if (q1.front() != nullptr)
					{
						q2.push(q1.front()->_pLeft);
						q2.push(q1.front()->_pRight);
					}
					q1.pop();
				}
				std::swap(q1, q2);
				h++;
			}
			q1.empty();
			q2.empty();
			return h - 1;
		}
		// 左单旋
		void RotateL(Node* pParent) {
			//新的头节点
			Node* new_Parent = pParent->_pRight;
			//旧头结点的父节点
			Node* grand = pParent->_pParent;
			//修改pParent的父节点
			if (grand != nullptr)
			{
				if (grand->_pLeft == pParent)
					grand->_pLeft = new_Parent;
				else
					grand->_pRight = new_Parent;
			}
			else {
				_pRoot = new_Parent;
			}
			// 修改pParent节点
			pParent->_pParent = new_Parent;
			pParent->_pRight = new_Parent->_pLeft;

			//修改new_Parent节点
			if(new_Parent->_pLeft!=nullptr)
				new_Parent->_pLeft->_pParent = pParent;
			new_Parent->_pLeft = pParent;
			new_Parent->_pParent = grand;
			pParent->_pParent = new_Parent;

			pParent->_bf = new_Parent->_bf = 0;
		}
		// 右单旋
		void RotateR(Node* pParent) {
			//新的头节点
			Node* new_Parent = pParent->_pLeft;
			//旧头结点的父节点
			Node* grand = pParent->_pParent;
			//修改pParent的父节点
			if (grand != nullptr)
			{
				if (grand->_pLeft == pParent)
					grand->_pLeft = new_Parent;
				else
					grand->_pRight = new_Parent;
			}
			else {
				_pRoot = new_Parent;
			}
			// 修改pParent节点
			pParent->_pParent = new_Parent;
			pParent->_pLeft = new_Parent->_pRight;

			//修改new_Parent节点
			if(new_Parent->_pRight!=nullptr)
				new_Parent->_pRight->_pParent = pParent;
			new_Parent->_pRight = pParent;
			new_Parent->_pParent = grand;
			pParent->_pParent = new_Parent;

			pParent->_bf = new_Parent->_bf = 0;
		}
		// 右左双旋
		void RotateRL(Node* pParent) {
			RotateR(pParent);
			RotateL(pParent->_pParent->_pParent);
		}
		// 左右双旋
		void RotateLR(Node* pParent) {
			RotateL(pParent);
			RotateR(pParent->_pParent->_pParent);
		}
	protected:
		Node* _pRoot;

	};
}

结语

截止至结语,本文已有接近1万字,制作不易可以留个免费的赞吗

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

js逆向-某投资平台参数分析

声明 本文仅供学习参考&#xff0c;如有侵权可私信本人删除&#xff0c;请勿用于其他途径&#xff0c;违者后果自负&#xff01; 如果觉得文章对你有所帮助&#xff0c;可以给博主点击关注和收藏哦&#xff01; 分析 aHR0cDovLzIyMS4yMTQuOTQuNTE6ODA4MS9pY2l0eS9pcHJvL2hhb…

4.5网安学习第四阶段第五周回顾(个人学习记录使用)

本周重点 ①部署域环境&#xff08;Win2008&#xff09; ②域组策略 ③域内信息收集 ④(重点)哈希传递攻击PTH ⑤MS14-068 提权漏洞 ⑥黄金票据伪造 ⑦白银票据伪造 ⑧ZeroLogon (CVE-2020-1472) 漏洞复现 本周主要内容 ①部署域环境&#xff08;Win2008&#xff09;…

.net core WebApi 部署 IIS

安装 IIS 下载需要的 net 版本安装 前往 .net core WebApi 项目打包 Program.cs var builder WebApplication.CreateBuilder(args);// 输出 builder.Services.AddControllers().AddJsonOptions(options > {options.JsonSerializerOptions.PropertyNamingPolicy null;…

.NET_NLog

步骤 1. 添加依赖 ①Microsoft.Extensions.DependencyInjection ②NLog.Extensions.Logging&#xff08;或Microsoft.Extensions.Logging.___&#xff09; Tutorial NLog/NLog Wiki GitHub 2.添加nlog.config文件(默认名称, 可改为其他名称, 但需要另行配置) 文件的基础…

第13节 第二种shellcode编写实战(2)

在第二种shellcode编写实战(1)的基础上&#xff0c;新增加一个CAPI类&#xff0c;将所有用到的函数都在这个类中做动态调用的处理&#xff0c;这样使得整个shellcode功能结构更加清晰。 1. 新建类CAPI&#xff08;即api.h和api.cpp两个文件&#xff09;&#xff1a; api.h&…

绍兴ISO27001认证:信息安全认证的金钥匙

&#x1f308;&#x1f308;绍兴ISO27001认证&#xff1a;✌️信息安全认证的金钥匙&#x1f511; &#x1f498;随着信息技术的飞速发展&#xff0c;&#x1f481;信息安全问题日益凸显。&#x1f510;为了提升信息安全管理水平&#xff0c;&#x1f46e;保障企业数据资产安全…

PROTEUS仿真软件的使用及存储器的设计

proteus proteus&#xff0c;即EDA工具软件。Proteus软件是英国Lab Center Electronics公司出版的EDA工具软件。它不仅具有其它EDA工具软件的仿真功能&#xff0c;还能仿真单片机及外围器件。它是比较好的仿真单片机及外围器件的工具。虽然国内推广刚起步&#xff0c;但已受到…

SpringBoot基于微信小程序的星座配对(源码)

博主介绍&#xff1a;✌程序员徐师兄、10年大厂程序员经历。全网粉丝12W、csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅&#x1f447…

数列排序C++

题目&#xff1a; 思路&#xff1a; 创建一个数组a&#xff0c;循环遍历输入&#xff0c;然后使用函数sort进行上升排序&#xff0c;最后循环遍历输出a[i]. #include <bits/stdc.h> using namespace std; int main(){int a[201];int n;cin>>n;//输入for(int i0;i&l…

数据链路层——计算机网络学习笔记三

使用点对点信道的数据链路层 前言&#xff1a; 1.数据链路层的重要性&#xff1a;网络中的主机、路由器都必须实现数据连输层&#xff1b; 2.数据链路层中使用的信道&#xff1a; 点对点信道&#xff1a;这种信道是一对一的通信方式&#xff1b; 广播信道&#xff1a;使用一对多…

VMware虚拟机故障:“显示指定的文件不是虚拟磁盘“,处理办法

一、故障现象 由于虚拟机宕机&#xff0c;强制重新启动虚拟机后显示错误&#xff0c;没有办法启动虚拟机。 虚拟机有快照&#xff0c;执行快照还原&#xff0c;结果也不行&#xff0c;反复操作&#xff0c;在虚拟机文件目录出现很多莫名文件 二、故障原因 根据故障提示&#…

StarRocks 【新一代MPP数据库】

1、StarRocks 1.1、StarRocks 简介 StarRocks 是新一代极速全场景 MPP (Massively Parallel Processing&#xff0c;MPP数据库是一种基于大规模并行处理技术的数据库系统&#xff0c;旨在高效处理大量数据。) 数据库。StarRocks 的愿景是能够让用户的数据分析变得更加简单和敏…

【Cesium】Cesium核心类、坐标系与着色器简介

核心类&#xff1a; Viewer: Viewer 是 Cesium 中最基本的视图容器&#xff0c;用于显示地球、地图、三维场景等。它提供了创建和管理场景的功能&#xff0c;可以配置视图的各种属性和行为。 Scene: Scene 是 Cesium 中的核心类之一&#xff0c;代表了一个三维场景&#xff0c…

py黑帽子学习笔记_环境准备

1 下载os装os 下载一个kali虚机镜像然后用虚机管理软件创虚机&#xff0c;装完如下图&#xff0c;我用的版本是2024.1的版本kali-linux-2024.1-installer-amd64&#xff0c;可以从镜像站下载&#xff0c;官网下的慢还断网Index of /kali-images/kali-2024.1/ | 清华大学开源软…

序列到序列模型在语言识别Speech Applications中的应用 Transformer应用于TTS Transformer应用于ASR 端到端RNN

序列到序列模型在语言识别Speech Applications中的应用 A Comparative Study on Transformer vs RNN in Speech Applications 序列到序列(Seq2Seq)模型在语音识别(Speech Applications)中有重要的应用。虽然Seq2Seq模型最初是为了解决自然语言处理中的序列生成问题而设计的…

力扣例题(用栈实现队列)

目录 链接. - 力扣&#xff08;LeetCode&#xff09; 描述 思路 push pop peek empty 代码 链接. - 力扣&#xff08;LeetCode&#xff09; 描述 思路 push 例如我们将10个元素放入栈中&#xff0c;假设最左边为栈顶&#xff0c;最右侧为栈底 则为10,9,8,7,6,5,4,3,…

windows使用Docker-Desktop部署lobe-chat

文章目录 window安装docker-desktop下载和启动lobe-chatAI大语言模型的选择lobe-chat设置大模型连接 window安装docker-desktop docker-desktop下载地址 正常安装应用&#xff0c;然后启动应用&#xff0c;注意启动docker引擎 打开右上角的设置&#xff0c;进入Docker Engine设…

ZFS 文件系统结构及 ZFS 文件系统数据恢复

ZFS是一种革命性的文件系统&#xff0c;它遵循完全不同的文件系统管理方法&#xff0c;同时提供目前其他文件系统无法提供的新功能和优势。ZFS 可靠、可扩展且易于管理。 它放弃了卷的概念&#xff0c;从而摆脱了传统的文件系统原则。另外&#xff0c;ZFS 提供更复杂的存储池&…

torch_geometric安装(CPU版本)

①打开官方安装网址&#xff1a;https://pytorch-geometric.readthedocs.io/en/2.3.0/install/installation.html ②对根据Pytorch选择相应版本。此前一直用CUDA不成功&#xff0c;这次使用CPU版本&#xff08;因为不用对应cuda&#xff0c;pytorchcudageometric三者对应起来很…

线下线上陪玩APP小程序H5搭建设计-源码交付,支持二开!

一、电竞陪玩系统APP的概念 电竞陪玩系统APP是一种专门为电子竞技玩家提供服务的平台。通过这个平台&#xff0c;玩家可以找到专业的电竞陪玩者&#xff0c;他们可以帮助玩家提升游戏技能&#xff0c;提供游戏策略建议&#xff0c;甚至陪伴玩家一起进行游戏。这种服务不仅可以提…