<C++> 二叉搜索树

目录

二叉搜索树

1. 概念

2. 二叉搜索树操作

2.1 基础结构

2.2 非递归版

1. 查找

2. 插入

3. 删除

2.3 递归版

1. 查找

2. 插入

3. 删除

2.4 拷贝构造函数

2.5 赋值运算符重载

2.6 析构函数

2.7 完整代码

3. 二叉搜索树的应用

4. 二叉搜索树的性能


二叉搜索树

1. 概念

二叉搜索树又称二叉排序树,它或者是一棵空树,或者是具有以下性质的二叉树:

  • 若它的左子树不为空,则左子树上所有节点的值都小于根节点的值
  • 若它的右子树不为空,则右子树上所有节点的值都大于根节点的值
  • 它的左右子树也分别为二叉搜索树

由于二叉搜索树的性质,该树的中序遍历就是递增序列

 

2. 二叉搜索树操作

2.1 基础结构

	template<class K>struct BSTreeNode{BSTreeNode<K>* _left;BSTreeNode<K>* _right;K _key;BSTreeNode(const K& key):_key(key), _left(nullptr), _right(nullptr){}};template<class K>class BSTree{typedef BSTreeNode<K> Node;public:BSTree():_root(nullptr){}private:Node* _root;};

2.2 非递归版

1. 查找

  • key小于cur->_key,则往左子树找
  • key大于cur->_key,则往右子树找
		bool 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 true;}}return false;}
2. 插入

  • 插入要先找到合适的空位
  • 如果已经之前已经存在,那么return false
  • 找到空位之后,new出一个新节点,在链接时我们要获取parent节点,这就需要我们提前记录parent节点
  • 链接时还要判断 key 与 parent->_key大小关系,因为我们找到了合适的位置,但是没有记录是在左还是在右
bool Insert(const K& key){//如果树为空,直接new一个根节点if (_root == nullptr){_root = new Node(key);return true;}//记录父节点,最后链到父节点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{return false;}}//由于不知道插入的节点与父亲的key大小关系,所以再判断一下是插到左边还是右边cur = new Node(key);if (parent->_key < key){parent->_right = cur;}else{parent->_left = cur;}return true;}
3. 删除

 

  • 先查找该节点是否存在,如果存在,再看以下几点
  • 删除节点情况分三种:该节点左孩子为空、该节点右孩子为空、该节点左右孩子都有
  • 左孩子为空:将右节点链接到parent上即可,此时还要判断cur在parent的哪一边,如果cur在parent的右边,那么cur的所有孩子必然比parent的key大,所以将cur的right链接到parent的right即可
  • 右孩子为空:同理
  • 左右孩子都存在:此时我们可以找该节点左子树的最右节点,或右子树的最左节点,这两个节点都是最接近根节点key值的节点(因为二叉搜索树的性质,当节点无穷时,这两个节点的key值将从左和从右边无限趋近于根节点的key值),找到节点后与根节点交换key值,此时如果是左子树的最右节点,那么该节点是绝不可能存在右子树,所以此时将该节点的左子树链接到parent节点即可(提前记录parent,在判断cur在parent的哪一边)
  • 最后不要忘了delete被删除的节点
bool Erase(const K& key){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  //再看看cur在父节点parent的哪一边,在哪一边就把孤儿链接到哪一边{if (parent->_right == cur){parent->_right = cur->_right;}else{parent->_left = cur->_right;}}}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;}}}else  //该节点左右都不为空{Node* parent = cur;Node* leftMax = cur->_left;while (leftMax->_right){parent = leftMax;leftMax = leftMax->_right;}swap(leftMax->_key, cur->_key);if (parent->_left == leftMax){parent->_left = leftMax->_left;}else{parent->_right = leftMax->_left;}cur = leftMax;}delete cur;return true;}}return false;}

2.3 递归版

        由于用户不传递root参数,所以FindR内层封装一个获取root参数的函数,其他函数同理

1. 查找

  • 如果找不到返回false,找到了返回true。比key小,往右子树递归找;比key大,往左子树递归找。
		bool FindR(const K& key){return _FindR(_root, key);}bool _FindR(Node* root, const K& key){if (root == nullptr)return false;if (root->_key < key){return _FindR(root->_right, key);}else if (root->_key > key){return _FindR(root->_left, key);}else{return true;}}
2. 插入

  • insert函数神之一手的地方就是参数类型 Node*&,当它递归下去时 root 其实是它的父节点的 left 或 right 的引用!它可以不用提前保存parent的信息,直接链接新节点!
		bool InsertR(const K& key){return _InsertR(_root, key);}//Node*& root,这里的引用是神之一手,因为在连接的时候可以不用再去记录parent//在链接的时候,那个root是引用的父亲的root->_right或root->_left !!bool _InsertR(Node*& root, const K& key){if (root == nullptr){//这个root是引用的父亲的root->_right或root->_left,这一步是直接链接了!root = new Node(key);return true;}if (root->_key < key){return _InsertR(root->_right, key);}else if (root->_key > key){return _InsertR(root->_left, key);}else{//已经有该节点了,返回falsereturn false;}}
3. 删除

  • 先找到要删除的节点,然后再提前保存该节点,为了后续的delete
  • 同样的,erase的参数也是Node*&,它极大方便了节点的链接
  • 找到节点后,如果该节点的左节点为空,那么直接root = root->_right;如果右节点为空,那么 root = root->_left,这就是Node* &的强大之处。那么可能有疑问,非递归版本为什么不能用引用?循环版本不能使用引用,是因为引用不能改变指向!递归可以使用引用是因为每次都是一个新的栈帧
  • 如果左右节点都存在,找左子树最右节点,交换key值,再erase掉key的节点(注意,不能从root开始找,因为root此时已经被交换key值了,递归会往右边去找,这就会导致找错了
  • 最后Erase()这里不能传leftMax->_left,因为leftMax是局部变量,最后子节点会链接不上真正的root

		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){return _EraseR(root->_right, key);}else if (root->_key < key){return _EraseR(root->_left, key);}else  //找到了,开始删除{//提前记录Node* del = root;//左为空if (root->_left == nullptr){root = root->_right;}//右为空else if (root->_right == nullptr){root = root->_left;}//左右都不为空else{Node* leftMax = root->_left;while (leftMax->_right){leftMax = leftMax->_right;}swap(leftMax->_key, root->_key);//这里不能传leftMax->_left,因为是别名,leftMax是局部变量,最后子节点会链接不上真正的rootreturn _EraseR(root->_left, key);}delete del;return true;}}

2.4 拷贝构造函数

  • 根据前序序列递归拷贝
  • 该递归就是从最左边开始链接,再从最底层往上

		BSTree(const BSTree<K>& t){_root = Copy(t._root);}Node* Copy(Node* root){//前序遍历拷贝if (root == nullptr)return nullptr;Node* copyroot = new Node(root->_key);copyroot->_left = Copy(root->_left);copyroot->_right = Copy(root->_right);return copyroot;}

2.5 赋值运算符重载

  • 现代写法
		BSTree<K>& operator=(BSTree<K> t){swap(_root, t._root);return *this;}

2.6 析构函数

  • 递归式析构
		~BSTree(){Destory(_root);}void Destory(Node*& root){if (root == nullptr)return;Destory(root->_left);Destory(root->_right);delete root;root = nullptr;}

2.7 完整代码

namespace key
{template<class K>struct BSTreeNode{BSTreeNode<K>* _left;BSTreeNode<K>* _right;K _key;BSTreeNode(const K& key):_key(key), _left(nullptr), _right(nullptr){}};template<class K>class BSTree{typedef BSTreeNode<K> Node;public:BSTree():_root(nullptr){}BSTree(const BSTree<K>& t){_root = Copy(t._root);}BSTree<K>& operator=(BSTree<K> t){swap(_root, t._root);return *this;}~BSTree(){Destory(_root);}bool Insert(const K& key){//如果树为空,直接new一个根节点if (_root == nullptr){_root = new Node(key);return true;}//记录父节点,最后链到父节点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{return false;}}//由于不知道插入的节点与父亲的key大小关系,所以再判断一下是插到左边还是右边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->_left;}else if (cur->_key < key){cur = cur->_right;}else{return true;}}return false;}bool Erase(const K& key){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  //再看看cur在父节点parent的哪一边,在哪一边就把孤儿链接到哪一边{if (parent->_right == cur){parent->_right = cur->_right;}else{parent->_left = cur->_right;}}}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;}}}else  //该节点左右都不为空{Node* parent = cur;Node* leftMax = cur->_left;while (leftMax->_right){parent = leftMax;leftMax = leftMax->_right;}swap(leftMax->_key, cur->_key);if (parent->_left == leftMax){parent->_left = leftMax->_left;}else{parent->_right = leftMax->_left;}cur = leftMax;}delete cur;return true;}}return false;}void InOrder(){_InOrder(_root);cout << endl;}bool FindR(const K& key){return _FindR(_root, key);}bool InsertR(const K& key){return _InsertR(_root, key);}bool EraseR(const K& key){return _EraseR(_root, key);}private:Node* Copy(Node* root){//前序遍历拷贝if (root == nullptr)return nullptr;Node* copyroot = new Node(root->_key);copyroot->_left = Copy(root->_left);copyroot->_right = Copy(root->_right);return copyroot;}void Destory(Node*& root){if (root == nullptr)return;Destory(root->_left);Destory(root->_right);delete root;root = nullptr;}bool _EraseR(Node*& root, const K& key){if (root == nullptr)return false;if (root->_key < key){return _EraseR(root->_right, key);}else if (root->_key < key){return _EraseR(root->_left, key);}else  //找到了,开始删除{//提前记录Node* del = root;//左为空if (root->_left == nullptr){root = root->_right;}//右为空else if (root->_right == nullptr){root = root->_left;}//左右都不为空else{Node* leftMax = root->_left;while (leftMax->_right){leftMax = leftMax->_right;}swap(leftMax->_key, root->_key);//这里不能传leftMax->_left,因为是别名,leftMax是局部变量,最后子节点会链接不上真正的rootreturn _EraseR(root->_left, key);}delete del;return true;}}//Node*& root,这里的引用是神之一手,因为在连接的时候可以不用再去记录parent//在链接的时候,那个root是引用的父亲的root->_right或root->_left !!bool _InsertR(Node*& root, const K& key){if (root == nullptr){//这个root是引用的父亲的root->_right或root->_left,这一步是直接链接了!root = new Node(key);return true;}if (root->_key < key){return _InsertR(root->_right, key);}else if (root->_key > key){return _InsertR(root->_left, key);}else{//已经有该节点了,返回falsereturn false;}}bool _FindR(Node* root, const K& key){if (root == nullptr)return false;if (root->_key < key){return _FindR(root->_right, key);}else if (root->_key > key){return _FindR(root->_left, key);}else{return true;}}void _InOrder(Node* root){if (root == NULL){return;}_InOrder(root->_left);cout << root->_key << " ";_InOrder(root->_right);}private:Node* _root;};void TestBSTree1(){int a[] = { 8, 3, 1, 10, 6, 4, 7, 14, 13 };BSTree<int> t;for (auto e : a){t.Insert(e);}t.InOrder();t.EraseR(4);t.InOrder();t.EraseR(6);t.InOrder();t.EraseR(7);t.InOrder();t.EraseR(3);t.InOrder();for (auto e : a){t.Erase(e);}t.InOrder();}}

3. 二叉搜索树的应用

1. K模型:K模型即只有key作为关键码,结构中只需要存储Key即可,关键码即为需要搜索到的值

比如:给一个单词word,判断该单词是否拼写正确,具体方式如下:

  • 以单词集合中的每个单词作为key,构建一棵二叉搜索树
  • 在二叉搜索树中检索该单词是否存在,存在则拼写正确,不存在则拼写错误。

2. KV模型:每一个关键码key,都有与之对应的值Value,即<Key, Value>的键值对。该种方式在现实生活中非常常见:比如英汉词典就是英文与中文的对应关系,通过英文可以快速找到与其对应的中文,英文单词与其对应的中文<word, chinese>就构成一种键值对;再比如统计单词次数,统计成功后,给定单词就可快速找到其出现的次数,单词与其出现次数就是<word, count>就构成一种键值对

比如:实现一个简单的英汉词典dict,可以通过英文找到与其对应的中文,具体实现方式如下:

  • <单词,中文含义>为键值对构造二叉搜索树,注意:二叉搜索树需要比较,键值对比较时只比较
  • Key查询英文单词时,只需给出英文单词,就可快速找到与其对应的key
namespace key_value
{template<class K, class V>struct BSTreeNode{BSTreeNode<K, V>* _left;BSTreeNode<K, V>* _right;K _key;V _value;BSTreeNode(const K& key, const V& value):_left(nullptr), _right(nullptr), _key(key), _value(value){}};template<class K, class V>class BSTree{typedef BSTreeNode<K, V> Node;public:BSTree():_root(nullptr){}void InOrder(){_InOrder(_root);cout << endl;}Node* FindR(const K& key){return _FindR(_root, key);}bool InsertR(const K& key, const V& value){return _InsertR(_root, key, value);}bool EraseR(const K& key){return _EraseR(_root, key);}private:bool _EraseR(Node*& root, const K& key){if (root == nullptr)return false;if (root->_key < key){return _EraseR(root->_right, key);}else if (root->_key > key){return _EraseR(root->_left, key);}else{Node* del = root;// 1、左为空// 2、右为空// 3、左右都不为空if (root->_left == nullptr){root = root->_right;}else if (root->_right == nullptr){root = root->_left;}else{Node* leftMax = root->_left;while (leftMax->_right){leftMax = leftMax->_right;}swap(root->_key, leftMax->_key);return _EraseR(root->_left, key);}delete del;return true;}}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->_right, key, value);}else if (root->_key > key){return _InsertR(root->_left, key, value);}else{return false;}}Node* _FindR(Node* root, const K& key){if (root == nullptr)return nullptr;if (root->_key < key){return _FindR(root->_right, key);}else if (root->_key > key){return _FindR(root->_left, key);}else{return root;}}void _InOrder(Node* root){if (root == NULL){return;}_InOrder(root->_left);cout << root->_key << ":" << root->_value << endl;_InOrder(root->_right);}private:Node* _root;};void TestBSTree1(){//BSTree<string, Date> carTree;BSTree<string, string> dict;dict.InsertR("insert", "插入");dict.InsertR("sort", "排序");dict.InsertR("right", "右边");dict.InsertR("date", "日期");string str;while (cin >> str){BSTreeNode<string, string>* ret = dict.FindR(str);if (ret){cout << ret->_value << endl;}else{cout << "无此单词" << endl;}}}void TestBSTree2(){// 统计水果出现的次数string arr[] = { "西瓜", "西瓜", "苹果", "西瓜", "苹果", "苹果", "西瓜", "苹果", "香蕉", "苹果", "香蕉" };BSTree<string, int> countTree;for (auto& str : arr){auto ret = countTree.FindR(str);if (ret == nullptr){countTree.InsertR(str, 1);}else{ret->_value++;}}countTree.InOrder();}
}

相比只有key版本,key_value只是在结构体内多加了value而已,部分函数原理也没有变

4. 二叉搜索树的性能

        插入和删除操作都必须先查找,查找效率代表了二叉搜索树中各个操作的性能。

        对有n个结点的二叉搜索树,若每个元素查找的概率相等,则二叉搜索树平均查找长度是结点在二叉搜索树的深度的函数,即结点越深,则比较次数越多。

        但对于同一个关键码集合,如果各关键码插入的次序不同,可能得到不同结构的二叉搜索树:

 

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

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

相关文章

Debug-023-Document.createElement()的使用

Document.createElement() document.createElement()是在对象中创建一个对象&#xff0c;要与appendChild() 或 insertBefore()方法联合使用。 appendChild() 方法在节点的子节点列表末添加新的子节点。 insertBefore() 方法在节点的子节点列表任意位置插入新的节点。 用途举…

笔记整理—uboot启动过程(3)栈的二次设置以及常用名词解析,BL1部分完

前文说到了uboot的lowlevel_init都干了些什么&#xff0c;也就是经过了这项初期的低级启动&#xff0c;使得我们能在串口监视器上看见机器打印出的第一句话“OK”。当lowlevel_init结束后&#xff0c;uboot去做了另一件事情&#xff0c;那就是栈的再次设置。 第一次栈设置发生在…

解决Qt多线程中fromRawData函数生成的QByteArray数据不一致问题

解决Qt多线程中fromRawData函数生成的QByteArray数据不一致问题 目录 &#x1f514; 问题背景&#x1f4c4; 问题代码❓ 问题描述&#x1fa7a; 问题分析✔ 解决方案 &#x1f514; 问题背景 在开发一个使用Qt框架的多线程应用程序时&#xff0c;我们遇到了一个棘手的问题&…

使用 LangGraph 构建工作流, 实现与虚拟女友对话

文章目录 简介背景流程图代码实现 简介 介绍了如何使用 LangGraph 搭建一个基于聊天机器人的工作流&#xff0c;具体实现了一个虚拟女友的角色扮演游戏。 通过流程图展示了构建完成的状态图&#xff0c;并介绍了各个节点的功能&#xff0c;如接收用户输入、生成对话等。提供了…

如何使用ssm实现保险业务管理系统设计与实现

TOC ssm131保险业务管理系统设计与实现jsp 绪论 1.1 研究背景 当前社会各行业领域竞争压力非常大&#xff0c;随着当前时代的信息化&#xff0c;科学化发展&#xff0c;让社会各行业领域都争相使用新的信息技术&#xff0c;对行业内的各种相关数据进行科学化&#xff0c;规…

ArcGIS Pro基础:设置2个窗口同步联动界面

如上所示&#xff0c;通过1步骤&#xff0c;新建了2个地图窗口&#xff0c;得到2和3所表示的【地图1】、【地图2】&#xff0c;一个是影像图&#xff0c;另一个是地形图&#xff0c; 假如有个需求&#xff0c;是将2个窗口联动起来&#xff1a;在观察影像的同时&#xff0c;也同…

[000-01-022].第06节:RabbitMQ中的交换机介绍

1.什么是Exchanges(交换机&#xff09;: 1.RabbitMQ 消息传递模型的核心思想是: 生产者生产的消息从不会直接发送到队列。实际上&#xff0c;通常生产者甚至都不知道这些消息传递传递到了哪些队列中2.生产者只能将消息发送到交换机(exchange)&#xff0c;交换机工作的内容非常…

Android Room DataBase

Room数据库是在Sqlite的基础上&#xff0c;进行了封装和优化。这让我们可以摆脱&#xff0c;繁琐的数据库操作 在module的gradle里面&#xff0c;加入: dependencies {annotationProcessor "androidx.room:room-compiler:2.3.0"implementation androidx.room:room-…

调用股票网站接口读取大A数据——个股资金流入趋势

以某股票为例&#xff0c;调用自定义的一个类&#xff0c;读取数据。 class BigAData:# 获取资金流向数据def get_money_flow(self, stock_code, page1, num20, sortopendate, asc0):该函数通过股票代码从新浪财经API获取资金流向数据。参数包括股票代码、页数、每页数量、排序…

jenkins最佳实践(一):jenkins安装与部署

各位小伙伴们大家好呀&#xff0c;我是小金&#xff0c;下面我将记录学习jenkins的系列文章与心得&#xff0c;一方面用于博主的自我记录&#xff0c;一方面如果能帮助到正在浏览这篇文章的小伙伴&#xff0c;那更好不过了&#xff0c;本篇文章主要讲述jenkins的安装以及安装je…

Redis篇一:初识Redis

文章目录 前言1. 初始Redis2. MySQL VS Redis3. 什么是分布式系统&#xff08;也是一种处理大量数据时的处理方式&#xff09;3.1 单机架构3.2 数据库与应用服务分离3.3 负载均衡3.4 数据库读写分离3.5 引入缓存&#xff08;Redis&#xff09;3.6 数据库分库分表3.7 引入微服务…

计算机毕业设计选题推荐-OA办公管理系统-Java/Python项目实战

✨作者主页&#xff1a;IT毕设梦工厂✨ 个人简介&#xff1a;曾从事计算机专业培训教学&#xff0c;擅长Java、Python、微信小程序、Golang、安卓Android等项目实战。接项目定制开发、代码讲解、答辩教学、文档编写、降重等。 ☑文末获取源码☑ 精彩专栏推荐⬇⬇⬇ Java项目 Py…

白酒与青年文化:潮流与传统的碰撞

在时代的洪流中&#xff0c;青年文化如同一股涌动的潮流&#xff0c;不断冲击着传统的边界。而白酒&#xff0c;作为中国传统文化的瑰宝&#xff0c;也在这一潮流中找到了新的表达方式。今天&#xff0c;我们就来探讨一下白酒与青年文化之间的碰撞与整合&#xff0c;以及豪迈白…

【Vue3】编程式路由导航

【Vue3】编程式路由导航 背景简介开发环境开发步骤及源码总结 背景 随着年龄的增长&#xff0c;很多曾经烂熟于心的技术原理已被岁月摩擦得愈发模糊起来&#xff0c;技术出身的人总是很难放下一些执念&#xff0c;遂将这些知识整理成文&#xff0c;以纪念曾经努力学习奋斗的日…

微服务事务管理

1.分布式事务问题 1.1.本地事务 本地事务&#xff0c;也就是传统的单机事务&#xff0c;在传统数据库事务中&#xff0c;必须要满⾜四个原则&#xff1a; 1.2.分布式事务 分布式事务&#xff0c;就是指不是在单个服务或单个数据库架构下&#xff0c;产⽣的事务&#xff0c;例…

全感知、全覆盖、全智能的名厨亮灶开源了

简介 AI视频监控平台, 是一款功能强大且简单易用的实时算法视频监控系统。愿景在最底层打通各大芯片厂商相互间的壁垒&#xff0c;省去繁琐重复的适配流程&#xff0c;实现芯片、算法、应用的全流程组合&#xff0c;减少企业级应用约 95%的开发成本&#xff0c;在强大视频算法加…

数学基础(七)

一、熵 熵代表物体内部的混乱程度。&#xff08;一件事发生的不确定性&#xff09; 熵应用到分类任务中 二、激活函数 Sigmoid函数&#xff1a; Tanh函数&#xff1a; Relu函数&#xff1a; 三、回归分析 回归分析是寻找存在相关关系的变量间的数学表达式&#xff0c;并进行…

[数据集][目标检测]电力场景输电线异物检测数据集VOC+YOLO格式2060张1类别

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;2060 标注数量(xml文件个数)&#xff1a;2060 标注数量(txt文件个数)&#xff1a;2060 标注…

Spring Data JPA 中分页Pageable 的使用说明

我 | 在这里 ⭐ 全栈开发攻城狮、全网10W粉丝、2022博客之星后端领域Top1、专家博主。 &#x1f393;擅长 指导毕设 | 论文指导 | 系统开发 | 毕业答辩 | 系统讲解等。已指导60位同学顺利毕业 ✈️个人公众号&#xff1a;热爱技术的小郑。回复 Java全套视频教程 或 前端全套视频…

黑神话悟空什么配置可以玩?什么样的游戏本配置可以畅玩《黑神话:悟空》?黑神话悟空电脑配置推荐

相信不少游戏爱好者&#xff0c;近期被《黑神话&#xff1a;悟空》这款游戏刷屏了&#xff0c;预售开启不到5分钟&#xff0c;所有的产品即宣告售罄&#xff0c;预购3天销售额就破亿&#xff0c;并迅速登顶Steam全球榜。作为一款备受期待的国产3A游戏&#xff0c;以其精美的画面…