C++【二叉搜索树】

news2024/11/22 23:29:09

文章目录

  • 一、二叉搜索树
    • (1)概念
    • (2)操作
    • (3)应用
  • 二、BST模拟实现及函数解析
    • (1)构建BST结点结构体
    • (2)BST默认构造及拷贝构造
    • (3)BST赋值重载和析构函数
    • (4)非递归实现BST三种操作
      • (1)查找
      • (2)删除
      • (3)插入
    • (5)递归实现BST三种操作
      • (1)查找
      • (2)插入
      • (3)删除
  • 三、BST实现源代码
    • (1)BST.h
    • (2)Test.cpp
  • 四、BST运行结果
  • 五、二叉搜索树的性能

一、二叉搜索树

(1)概念

二叉搜索树别名二叉排序树建成BST,它可能是一棵空树,也可能过是具有以下性质的二叉树:如果它的左子树不为空,则左子树上所有节点的值都小于根节点的值,如果它的右子树不为空,则右子树上所有节点的值都大于根节点的值,而且它的左右子树也分别为二叉搜索树。如图:
在这里插入图片描述

(2)操作

二叉树有三种操作:查找,插入,删除。
查找:从根开始比较,查找,比根大则往右边走查找,比根小则往左边走查找,最多查找高度次,走到到空,还没找到,这个值不存在。
插入:树为空,则直接新增节点,树不空,按二叉搜索树性质查找插入位置,插入新节点
删除
先查找元素是否在二叉搜索树中,如果不存在,则返回, 反之要删除的结点可能
有三种情况:把叶子结点归到了第一和第二种情况。
1.如果删除结点右为空,删除该结点且使被删除节点的父结点指向被删除节点的左孩子结点。
2.如果删除结点左为空,删除该结点且使被删除节点的父结点指向被删除结点的右孩子结点,也和第一种一样都是直接删除。
3.找左子树的最大结点(最右结点)找右子树的最小结点(最左节点),用它的值填补到被删除节点中,再来处理该结点的删除问题,相当于替换法删除,间接删。

(3)应用

有两种模型:
K模型:K模型即只有key作为关键码,结构中只需要存储Key即可,关键码即为需要搜索到
的值。
举例:给一个单词hello,判断该单词是否拼写正确,具体方式如下:
以词库中所有单词集合中的每个单词作为key,构建一棵二叉搜索树,在二叉搜索树中检索该单词是否存在,存在则拼写正确,不存在则拼写错误。
2. KV模型:每一个关键码key,都有与之对应的值Value,即<Key, Value>的键值对。
举例:英汉词典就是英文与中文的对应关系,通过英文可以快速找到与其对应的中文,英文单词与其对应的中文<word, chinese>就构成一种键值对;
再比如统计单词次数,统计成功后,给定单词就可快速找到其出现的次数,单词与其出现次数就是<word, count>就构成一种键值对。

二、BST模拟实现及函数解析

(1)构建BST结点结构体

template<class K>
	struct BSTNode
	{
		BSTNode<K>* left;
		BSTNode<K>* right;
		K _key;
		BSTNode(const K& t)
			:_key(t)
			,left(nullptr)
			,right(nullptr)
		{}
		
	};

结点组成树,所以现构建结构体,在里面添加属性,他需要有左指针left和右指针right分别指向左孩子和右孩子,需要存储结点的值key,这里需要对它们进行初始化,所以还在里面写一个构造函数。

(2)BST默认构造及拷贝构造

        BST() = default;
        BST(const BST<K>& t)
		{
			_root = copy(t._root);
		}
		Node* copy(Node* root)
			{
				if(!root)
				{
					return nullptr;
				}
				Node* newroot = new Node(root->_key);
				newroot->left = copy(root->left);
				newroot->right = copy(root->right);
				return newroot;
			}

我们在这用default强制生成默认构造,而拷贝构造涉及到深拷贝,我们再封装一个copy函数,如果为空直接返回nullptr,反之new出一个结点,再分别递归左子树和右子树,最后返回根结点。

(3)BST赋值重载和析构函数

BST<K>& operator=(const BST<K>& t)
		{
			swap(_root, t._root);
			return *this;
		}
		~BST()
		{
			destroy(_root);
		}
			void destroy(Node*& root)
			{
				if (!root)
					return;
				destroy(root->left);
				destroy(root->right);
				delete root;
				root = nullptr;
			}

对于赋值重载,同样也涉及深拷贝,只需要交换两个树的根节点的指针即地址即可。析构函数,需要逐一释放并最后置为空。

(4)非递归实现BST三种操作

(1)查找

       bool find(const K& key)
		{
			Node* cur = _root;
			while (cur)
			{
				if (cur->_key < key)
				{
					cur = cur->right;
				}
				else if (cur->_key > key)
				{
					cur = cur->left;
				}
				else
				{
					return true;
				}
			}
			return false;
		}

非递归法写查找,只需要用while循环和BST的性质,在循环里面:从根节点开始我比你大就往右走,同时更新当前的位置,我比小就往左走,同时更新当前的位置,否则找到返回真,走到空结束,遍历结束还没有找到返回假。

(2)删除

        bool erase(const K& key)
		{
			//叶子结点,左为空或右为空,可以把前面归到后面,实际上是两类。托孤,要记录父亲(删1和6)
			//第三种情况左右都不为空,请保姆,找左子树的最大结点(最右结点)找右子树的最小结点(最左节点),间接删,替代法。
			Node* parent = nullptr;
			Node* cur = _root;
			while (cur)
			{
				if (cur->_key < key)
				{
					parent = cur;
					cur = cur->right;
				}
				else if (cur->_key > key)
				{
					parent = cur;
					cur = cur->left;
				}
				else
				{
					if (cur->left == nullptr)//左为空
					{
						if (cur == _root)
						{
							_root = cur->right;
						}
						else
						{
							if (parent->right == cur)
							{
								parent->right = cur->right;
							}
							else
							{
								parent->left = cur->right;
							}
						}
						delete cur;

					}

					else if (cur->right == nullptr)//右为空
					{
						if (cur == _root)
						{
							_root = cur->left;
						}
						else
						{
							if (parent->right == cur)
							{
								parent->right = cur->left;
							}
							else
							{
								parent->left =cur->left;
							}
						}
						delete cur;
					}
					else//左右不为空,这里去找右子树的最左节点
					{
						Node* pmiR = cur;
						Node* miR = cur->right;
						while (miR->left)
						{
							pmiR = miR;
							miR = miR->left;
						}

						cur->_key = miR->_key;
						if (pmiR->right == miR)
						{
							pmiR->right = miR->right;
						}
						else
						{
							pmiR->left = miR->right;
						}
						delete miR;
					}
					return true;
				}
			}
			return false;
		}

删除思路就是先用BST性质进行找要删除的结点,比你大往右走,比你小往左走,这里还有记录一下父结点,以便后续删除,最后再用前面说的三种情况进行删除。
1、如果删除结点左为空,先去判断这个结点是不是根结点,如果是就结点的右子树给给根节点,让它充当根节点,再释放结点;如果不是根结点,我就在里面还要进行两次判断,如果是父亲节点的右孩子是当前结点,就把当前节点的右孩子和父亲的右指针链接,反之就把当前结点的右孩子和父亲的左指针链接。最后释放结点。如前面的图:删除1和6。
2、如果删除结点右为空,同样也是先去判断这个结点是不是根结点,如果是就结点的左子树给给根节点,让它充当根节点,再释放结点;如果不是根结点,需要在里面还要进行两次判断,如果是父亲节点的右孩子是当前结点,就把当前节点的左孩子和父亲的右指针链接,反之就把当前结点的左孩子和父亲的左指针链接。如前面的图:删除14。
3、如果删除结点左右都为空,这时候就需要用替代法,找一个合适的结点来充当现有结点。合适的结点值就要找一个左子树的最大结点即最右结点或右子树的最小结点即最左节点。这里是找的最右结点,遍历找的时候需要记录最小节点的父节点pmiR和最小结点miR,找到之后进行覆盖,把最小结点的值给要删除结点的值,最后进行链接,这里需要做出两种判断,如果父结点的左孩子是最小结点,需要把最小结点的右孩子和父亲的左指针链接,反之把最小结点的右孩子和父亲的右指针链接。

(3)插入

        bool insert(const K& key)
		{
			if (_root ==nullptr)
			{
				_root = new Node(key);
				return true;
			}
			Node* cur = _root;
			Node* parent = nullptr;
			while (cur)
			{
				if (key > cur->_key)
				{
					parent = cur;
					cur = cur->right;
				}
				else if (key < cur->_key)
				{
					parent = cur;
					cur = cur->left;
				}
				else
				{
					return false;
				}
			}

			cur = new Node(key);
			if (parent->_key < key)
			{
				parent->right = cur;
			}
			else
			{
				parent->left = cur;
			}
			return true;
		}

插入思路是先找到空进行插入,用BST性质,遍历左右子树,同时记录父亲结点,接着new新节点进行插入,需要两个判断如果比它大,就链接父结点右指针,反之链接父节点左指针。

(5)递归实现BST三种操作

(1)查找

        bool _Rfind(Node* root, const K& key)
		{
			if (!root)
				return false;
			if (root->_key = key)
				return true;
			if (root->_key < key)
				return _Rfind(root->right);
			else 
				return _Rfind(root->left);
		}

递归法查找思路还是利用BST性质,如果比它大就递归右子树,如果比它小就递归左子树,如果找找到返回真,为空返回假。

(2)插入

bool _Rinsert(Node*& root, const K& key)
		{
			if (!root)
			{
				root = new Node(key);
			}
			if (root->_key < key)
				return _Rinsert(root->right, key);
			if (root->_key > key)
				return _Rinsert(root->left, key);
		}

递归法插入,也是用BST性质,先递归左右子树,等到为空的时候进行插入,但插入要和父亲链接起来,怎么链接?在不增加参数的情况下我们可以传引用,是最优方案,因为就最后一次起了作用,实际上就是此时的root是上一层root左指针或右指针的别名,在这里new了一个结点给给root,间接地链接上了。

(3)删除

bool _Rerase(Node*& root, const K& key)
		{
			if (!root)
				return false;
			if (root->_key < key)
				return _Rerase(root->right, key);
			else if (root->_key > key)
				return _Rerase(root->left, key);
			else
			{
				Node* del = root;
				if (root->right == nullptr)
				{
					root = root->left;
				}
				else if (root->left == nullptr)
				{
					root = root->right;
				}
				else
				{
					Node* mal = root;
					mal = mal->left;
					
					while (mal->right)
					{
						mal = mal->right;
					}
					swap(root->_key, mal->_key);
					return _Rerase(root->left, mal->_key);

				}
				delete del;
				return true;
			}

		}

递归法删除,用BST性质进行找要删除的结点,递归左右子树,这里不需要记录父结点,同样也是三种情况。
1、删除结点左为空,定义一个del指针变量,把当前删除的结点给它,再进行链接,用了引用,不需要管父节点和哪个指针链接,直接链接,最释放del。
2、同理1。
3、删除结点左右都不为空,也是找一个合适的结点替代它,在这里找的是左子树的最大结点即最右结点,找到之后把删除结点和最大结点的值交换,重新再做一次递归转化为删掉左子树的最大结点,此时的mal存的key不会进入第三个条件,因为此时mal的孩子要么有一个要么没有,这样它会是第一种情况或是第二种情况,不管哪种情况我们能用到引用,找到父亲进而链接。
这里注意的这里第二次递归指针不能直接传mal,因为如果传了,我们的引用就没有用了就链接不了,因为它会进入第一个或第二条件,我们要的是上个位置的引用不是这个位置的引用。

三、BST实现源代码

(1)BST.h

#pragma once


namespace nza
{
	template<class K>
	struct BSTNode
	{
		BSTNode<K>* left;
		BSTNode<K>* right;
		K _key;
		BSTNode(const K& t)
			:_key(t)
			,left(nullptr)
			,right(nullptr)
		{}
		
	};
	template<class K>
	class BST
	{
		typedef BSTNode<K> Node;
	public:
		BST() = default;
		BST(const BST<K>& t)
		{
			_root = copy(t._root);

		}
		BST<K>& operator=(const BST<K>& t)
		{
			swap(_root, t._root);
			return *this;
		}
		~BST()
		{
			destroy(_root);
		}

		//非递归如下:
		bool insert(const K& key)
		{
			if (_root ==nullptr)
			{
				_root = new Node(key);
				return true;
			}
			Node* cur = _root;
			Node* parent = nullptr;
			while (cur)
			{
				if (key > cur->_key)
				{
					parent = cur;
					cur = cur->right;
				}
				else if (key < cur->_key)
				{
					parent = cur;
					cur = cur->left;
				}
				else
				{
					return false;
				}
			}

			cur = new Node(key);
			if (parent->_key < key)
			{
				parent->right = cur;
			}
			else
			{
				parent->left = cur;
			}
			return true;
		}

		bool find(const K& key)
		{
			Node* cur = _root;
			while (cur)
			{
				if (cur->_key < key)
				{
					cur = cur->right;
				}
				else if (cur->_key > key)
				{
					cur = cur->left;
				}
				else
				{
					return true;
				}
			}
			return false;
		}

		bool erase(const K& key)
		{
			//叶子结点,左为空或右为空,可以把前面归到后面,实际上是两类。托孤,要记录父亲(删1和6)
			//第三种情况左右都不为空,请保姆,找左子树的最大结点(最右结点)找右子树的最小结点(最左节点),间接删,替代法。
			Node* parent = nullptr;
			Node* cur = _root;
			while (cur)
			{
				if (cur->_key < key)
				{
					parent = cur;
					cur = cur->right;
				}
				else if (cur->_key > key)
				{
					parent = cur;
					cur = cur->left;
				}
				else
				{
					if (cur->left == nullptr)//左为空
					{
						if (cur == _root)
						{
							_root = cur->right;
						}
						else
						{
							if (parent->right == cur)
							{
								parent->right = cur->right;
							}
							else
							{
								parent->left = cur->right;
							}
						}
						delete cur;

					}

					else if (cur->right == nullptr)//右为空
					{
						if (cur == _root)
						{
							_root = cur->left;
						}
						else
						{
							if (parent->right == cur)
							{
								parent->right = cur->left;
							}
							else
							{
								parent->left =cur->left;
							}
						}
						delete cur;
					}
					else//左右不为空,这里去找右子树的最左节点
					{
						Node* pmiR = cur;
						Node* miR = cur->right;
						while (miR->left)
						{
							pmiR = miR;
							miR = miR->left;
						}

						cur->_key = miR->_key;
						if (pmiR->right == miR)
						{
							pmiR->right = miR->right;
						}
						else
						{
							pmiR->left = miR->right;
						}
						delete miR;
					}
					return true;
				}
			}
			return false;
		}


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

		//递归如下:
		bool Rfind(const K& key)
		{
			return _Rfind(_root, key);
		}
		bool Rinsert(const K& key)
		{
			return _Rinsert(_root, key);
		}
		bool Rerase(const K& key)
		{
			return _Rerase(_root, key);
		}

	protected:
		bool _Rfind(Node* root, const K& key)
		{
			if (!root)
				return false;
			if (root->_key = key)
				return true;
			if (root->_key < key)
				return _Rfind(root->right);
			else 
				return _Rfind(root->left);
		}
		bool _Rinsert(Node*& root, const K& key)
		{
			if (!root)//插入要和父亲链接起来,在不增加参数的情况下我们可以传引用,是最优方案,因为就最后一次起了作用,实际上就是
				//此时的root是上一层root指向左指针或右指针的别名,在这里new了一个结点给给root,间接地链接上了。
			{
				root = new Node(key);
			}
			if (root->_key < key)
				return _Rinsert(root->right, key);
			if (root->_key > key)
				return _Rinsert(root->left, key);
		}
		bool _Rerase(Node*& root, const K& key)
		{
			if (!root)
				return false;
			if (root->_key < key)
				return _Rerase(root->right, key);
			else if (root->_key > key)
				return _Rerase(root->left, key);
			else
			{
				Node* del = root;
				if (root->right == nullptr)
				{
					root = root->left;
				}
				else if (root->left == nullptr)
				{
					root = root->right;
				}
				else
				{
					Node* mal = root;
					mal = mal->left;
					
					while (mal->right)
					{
						mal = mal->right;
					}
					swap(root->_key, mal->_key);
					return _Rerase(root->left, mal->_key);//引用不能改指向,所以把它们的值交换,重新再做一次递归转化为删掉左子树的
					//最大结点,此时的mal存的key不会进入第三个条件,因为此时mal的孩子要么有一个要么没有。这里注意的这里第二次递归指针
					// 不能直接传mal,因为如果传了,我们的引用就没有用了就链接不了,因为它会进入第一个或第二条件,我们要的是上个位置的引用
					//不是这个位置的引用

				}
				delete del;
				return true;
			}

		}
			Node* copy(Node* root)
			{
				if(!root)
				{
					return nullptr;
				}
				Node* newroot = new Node(root->_key);
				newroot->left = copy(root->left);
				newroot->right = copy(root->right);
				return newroot;
			}
			void destroy(Node*& root)
			{
				if (!root)
					return;
				destroy(root->left);
				destroy(root->right);
				delete root;
				root = nullptr;
			}
			void _inoder(Node* root)
			{
				if (!root)
					return;
				_inoder(root->left);
				cout << root->_key << " ";
				_inoder(root->right);

			}

	private:
		Node* _root = nullptr;
		
	};
}

(2)Test.cpp

#include<iostream>
using namespace std;
#include"BST.h"
int main()
{
	nza::BST<int> t;
	int a[] = { 5,2,9,6,1,0,3 };
	for (auto n : a)
	{
		t.insert(n);
	}
	t.inorder();

	t.erase(9);
	t.inorder();

	t.erase(5);
	t.inorder();

	t.erase(1);
	t.inorder();


	t.Rerase(2);
	t.inorder();
	 
	t.Rerase(0);
	t.inorder();

	t.Rinsert(100);
	t.inorder();


}

四、BST运行结果

在这里插入图片描述

五、二叉搜索树的性能

插入和删除操作都必须先查找,查找效率代表了二叉搜索树中各个操作的性能。
二叉搜索树为完全二叉树(或者接近完全二叉树),次数为:O(logN)。
在这里插入图片描述
二叉搜索树为单支树,次数为:O(N^2)
在这里插入图片描述
如果退化成单支树,二叉搜索树的性能就失去了。是可以进行改进的,不论按照什么次序插入关键码,二叉搜索树的性能都能达到最优,而AVL树和红黑树就可以解决这个问题。

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

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

相关文章

2023亚马逊云科技行业峰会,汇聚行业专家分享行业数字化创新之道

从实验室扩展到真实世界&#xff0c;从前沿技术探索到医疗生命科学行业的快速创新实践&#xff0c;亚马逊云科技不断地通过数字化助力医疗和生命科学的行业创新。由上海徐汇区科委指导&#xff0c;上海枫林集团作为支持单位&#xff0c;亚马逊云科技主办的2023亚马逊云科技医疗…

yum仓库及NFS共享服务

部署yum仓库及NFS共享服务 一、yum仓库服务yum概述源种类&#xff1a; 二、安装源安装ftp源安装在线源本地源和在线源同时使用将软件包变成Yum源 三、NFS共享存储服务NFS架构NFS原理 四、NFS实操 一、yum仓库服务 yum概述 基于RPM包构建的软件更新机制可以自动解决依赖关系所…

LVS负载均衡群集部署—NAT

目录 一、群集的概述1、群集的含义2、出现高并发的解决方法3、群集的三种分类3.1负载均衡群集3.2高可用群集3.3高性能运算群集 4、负载均衡的结构 三、LVS调度器用的调度方法四、LVS的工作模式及其工作过程1.NAT模式&#xff08;VS-NAT&#xff09;2.直接路由模式&#xff08;V…

冲实习 or 全力准备秋招?

作者&#xff1a;阿秀 校招八股文学习网站&#xff1a;https://interviewguide.cn 这是阿秀的第「261」篇原创 小伙伴们大家好&#xff0c;我是阿秀。 欢迎今年参加秋招的小伙伴加入阿秀的学习圈&#xff0c;目前已经超过 2300 小伙伴加入&#xff01;去年认真准备和走下来的基…

ADKEY多按键制作阻值选择

参考链接 (10条消息) 【物尽其用】ADKEY多按键制作与经验分享_SimpleJY的博客-CSDN博客https://blog.csdn.net/qq_31247231/article/details/81013459其中的阻值选择经过仿真电路 仿真结果如下表 按键序号ad值键值差0409337113722907228151813263458742047975195028261668657…

数字硬件建模SystemVerilog-通信总线建模 --Interface方法

来到了SV最后一部分&#xff0c;预计三篇文章&#xff0c;两周更完&#xff0c;所有的思维导图如下&#xff1a; 概述 SystemVerilog Interface是modport的一种&#xff0c;但比简单的输入、输出或输入输出端口的功能更多。在其最简单的形式中&#xff0c;Interface端口将相关的…

第5章 数据结构之“链表”

链表简介 1.多个元素组成的列表。 2.元素的存储不连续&#xff0c;用next指针连在一起。 数组 vs 列表 数组&#xff1a;增删非手尾元素时往往需要移动元素。如果要在数组中增加一个元素&#xff0c;数组后面的所有元素需要往后面移动一位。如果在数组中删除一个元素&#x…

Hive ---- Hive 安装

Hive ---- Hive 安装 1. Hive安装地址2. Hive安装部署1. 安装Hive2. 启动并使用Hive 3. MySQL安装1. 安装MySQL2. 配置MySQL3. 卸载MySQL说明 4. 配置Hive元数据存储到MySQL1. 配置元数据到MySQL2. 验证元数据是否配置成功3. 查看MySQL中的元数据 5. Hive服务部署1. hiveserver…

旧版VS安装 Visual Studio 2019/2017/2015官方安装教程

安装VisualStudio找不到官方版本&#xff1f;只能找到第三方&#xff1f;害怕中毒&#xff1f; 不要急&#xff0c;本文例举了VS 2019 2017 2015的官方位置&#xff0c;不用但心装成第三方Visual Studio 百度搜索 Visual Studio 2017&#xff0c;只有第三方的包&#xff0c;而…

HBase(2):HBase数据模型

1 简介 在HBASE中&#xff0c;数据存储在具有行和列的表中。这是看起来关系数据库(RDBMS)一样&#xff0c;但将HBASE表看成是多个维度的Map结构更容易理解。 表结构如下&#xff1a; ROWKEY C1列蔟 C2列蔟 rowkey 列1 列2 列3 列4 列4 列6 列簇结构如下&#xff1a…

学系统集成项目管理工程师(中项)系列13a_人力资源管理(上)

1. 基本情况 1.1. 项目团队成员的特征 1.1.1. 高学历、高素质、流动性强、年轻、个性独立 1.2. IT行业的显著特征 1.2.1. 工作强度大 1.3. 绩效评定、招聘、留用、劳工关系、健康与安全规定及其他与管理人力资源有关的技能 1.4. 项目团队成员是项目的人力资源 1.5. 项目…

后端要一次性返回我10万条数据

问题描述 面试官&#xff1a;后端一次性返回10万条数据给你&#xff0c;你如何处理&#xff1f;我&#xff1a;歪嘴一笑&#xff0c;what the f**k! 问题考察点 看似无厘头的问题&#xff0c;实际上考查候选人知识的广度和深度&#xff0c;虽然在工作中这种情况很少遇到... …

【unity项目实战】3DRPG游戏开发06——敌人和攻击

导入敌人素材 RPG Monster Duo PBR Polyart 拖入敌人,如果觉得他的亮度有点低,选择他本体,可以选择调节修改他材质球metllic alpha的值 我们新建一个敌人脚本,我们的所有敌人都会挂载这个脚本,如果我们希望每个敌人都挂载导航组件,一个添加无疑是很麻烦的,我们可以使…

基于ESP32的zigbee温湿度采集系统

一. 系统设计及框图&#xff1a; 本设计可以实现通过ZIGBEE传输温湿度信息和开关量信号到云端然后再通过手机APP显示。系统分为发射端和接收端&#xff0c;发送端采集温湿度信息后通过ZIGBEE发送到接收端&#xff0c;接收端接收到温湿度信息后上传到云端并通过手机APP显示。 …

import _jpype ImportError: DLL load failed: 找不到指定的模块

问题记录及解决思路 &#xff08;1&#xff09;问题记录 当python3需要调用java程序的需求时&#xff0c;需要使用到jpype这个库&#xff0c;尝试通过pip install jpype 进行安装&#xff0c;结果提示没有这个库&#xff0c;看网上教程提示pip install jpype1进行安装还是有问…

游戏测试的整个流程是什么

在游戏行业中&#xff0c;游戏测试是一个非常重要的岗位&#xff0c;并且游戏测试的内容是比较复杂的&#xff0c;除了对游戏的整体流程有一定的了解之外&#xff0c;还需要对一些细节部分有所了解。那么&#xff0c;一个完整的游戏测试流程是怎么样的呢? 游戏测试其实并不是一…

扎克伯格:同时专注AI和Metaverse,Quest 3将是下一个里程碑

自更名以来&#xff0c;Meta大张旗鼓的重磅投入元宇宙面临了一系列风波&#xff0c;市场经济不景气&#xff0c;广告利润下降&#xff0c;大幅裁员、重组等。 23年1季度Meta实现营收增长&#xff0c;好于预期&#xff0c;而此前三个季度同比都是下跌&#xff0c;可见“效率年”…

【AI实战】微小目标检测模型SSPNet--训练环境从零开始搭建

【AI实战】微小目标检测模型SSPNet--训练环境从零开始搭建 SSPNet介绍环境搭建安装依赖参考 SSPNet介绍 SSPNet: Scale Selection Pyramid Network for Tiny Person Detection from UAV Images 官方连接 https://github.com/MingboHong/SSPNetarxiv https://arxiv.org/abs/210…

ValueError: Please specify `target_modules` in `peft_config`解决方案

大家好,我是爱编程的喵喵。双985硕士毕业,现担任全栈工程师一职,热衷于将数据思维应用到工作与生活中。从事机器学习以及相关的前后端开发工作。曾在阿里云、科大讯飞、CCF等比赛获得多次Top名次。现为CSDN博客专家、人工智能领域优质创作者。喜欢通过博客创作的方式对所学的…

hive进阶——在centos7里面配置mysql,将原来hive的客户端扩展

系列文章目录 centos集群上安装hive客户端的操作步骤以及hive介绍 文章目录 一、启动hive客户端 二、在Hive上面安装MySQL 2.1、MySQL安装 1 检查当前系统是否安装过 MySQL 2 将 MySQL安装包拷贝到 /opt/software目录下 3 解压 MySQL安装包 ​4 在安装目录下执行 rpm …