当前位置: 首页 > news >正文

数据结构——搜索二叉树

文章目录

  • 一. 概念
  • 二. 二叉搜索树的操作
    • 1.查找
    • 2.插入
    • 3.删除(重点)
    • 4.遍历
    • 5.拷贝构造与析构
  • 三.二叉搜索树的递归实现
    • 1.递归查找
    • 2.递归插入
    • 3.递归删除
  • 四.二叉搜索树的性能分析
  • 五.二叉树搜索的应用
  • 六.源码

前言:
本章我们将认识一种新的二叉树——搜索二叉树。这棵树有个神奇的功能就是会对数据自动排序且有着非常高的查找效率。搜索二叉树作为set、map的基础结构,同样又是接下来将要学到的AVL树以及红黑树的实现基础非常值得我们去深入学习~

一. 概念

叉搜索树本质上也是一种二叉树,只不过多了一个约束规则——

若一棵二叉树不为空,则:

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

所以构建一个搜索二叉树,只需要在插入结点时满足约束规则即可。

二. 二叉搜索树的操作

与二叉树相同,二叉搜索树由一个个结点链接而成。每个结点包含三个成员——

template <class K>
struct BSTreeNode
{BSTreeNode<K>* _left;  //左孩子BSTreeNode<K>* _right; //右孩子K _key;				   //键值BSTreeNode(const K& key):_left(nullptr), _right(nullptr), _key(key){}
};

所以再定义出BSTNode(Binary Search Tree简写)结构体——

template <class K>
class BSTree
{typedef BSTreeNode<K> Node;
public:// 成员函数的实现// 插入、删除、查找...
private:Node* _root = nullptr;
};

接着就是各种成员函数的实现了

1.查找

搜索二叉树的查找比较简单而且更容易帮助我们理解搜索二叉树的性质,所以先从查找入手。
在这里插入图片描述

以上图为例,倘若我们要查找 7,具体的思路是这样的——

  • 7 < 8,因此去 8 的左子树去查找
  • 7 > 3,因此去 3 的右子树去查找
  • 7 > 6,因此去 6 的右子树去查找
  • 7 = 7,找到了,返回true

于是我们试着着手实现一个Find函数

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;elsereturn true; // 找到返回true}return false; // 未找到返回false
}

2.插入

理解了如何查找,插入也就非常简单。

在这里插入图片描述

还是以此图为例,倘若我们要插入 9 ,具体步骤为——

  • 首先确定cur的位置,并随时更新parent
  • 最终,cur走到10的左节点的位置,即cur = nullptr,循环结束
  • 此时patent = Node*(10)
  • 最后一步,new一个结Node*(key)并赋值给parent->_left即可。
bool Insert(const K& key)
{// 如果是第一次插入,直接new一个新结点给_rootif (_root == nullptr){_root = new Node(key);return true;}Node* cur = _root; // cur用来定位插入的位置Node* parent = cur; // 记录parent的父亲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 Node(key);// 插入时依旧要进行判断if (parent->_key < key)parent->_right = cur;elseparent->_left = cur;return true;
}

3.删除(重点)

二叉搜索树的删除是最精华的部分。对与叶子节点,例如4、7、13,删除非常简单,只需将自身的位置替换为nullptr即可。

在这里插入图片描述

如果要删除14或者10,也是比较简单的,因为10的左右子树只有一方为nullptr(10的左子树为空),所以只需要载删除的时候让父结点接管自己不为空的子树即可。

倘若要删除6或者3,由于它们的左右子树都不为空,删除时无法将两个子树都交给父结点,情况就较为复杂。

所以此种情况,我们只能想办法请一个人来接替自己的位置,但是并不是谁来都能胜任这个位置的。这个接替者必须满足二叉搜索树的条件——左子树都比它小,右子树都比它大。那么这个接替者的人选只能有这两个——

  • 左子树的最大(最右)节点
  • 或右子树的最小(最左)节点

例如,倘若要删除3,此时有两种做法都可行——

  • 用1替换3
  • 用7替换3

综上所述,删除操作共分为一下几种情况——

  1. 左子树为空
  2. 右子树为空
  3. 左右子树都不为空
  4. (左右子树都为空其实可以归并到1或2的情况中)
bool Erase(const K& key)
{Node* cur = _root;Node* parent = cur;while (cur){// 找到值为key的结点if (cur->_key < key){parent = cur;cur = cur->_right;}else if (cur->_key > key){parent = cur;cur = cur->_left;}else // 找到了{	// 删除if (cur->_left == nullptr) // 1.左子树为空{if (cur == _root) // 根节点的删除{_root = cur->_right;return true;}else{if (parent->_left == cur)parent->_left = cur->_right;elseparent->_right = cur->_right;delete cur;}}else if (cur->_right == nullptr) // 2.右子树为空{if (cur == _root) // 根节点的删除{_root = cur->_left;return true;}else{if (parent->_left == cur)parent->_left = cur->_left;elseparent->_right = cur->_left;delete cur;}}else // 左右子树都不为空{// 找左子树的最大结点 或者 右子树的最小结点Node* minRight = cur->_right;Node* pminRight = cur;while (minRight->_left){pminRight = minRight;minRight = minRight->_left;}cur->_key = minRight->_key; // 替换if (pminRight->_left == minRight){pminRight->_left = minRight->_right;}else{pminRight->_right = minRight->_right;}delete minRight;}return true;}}return false;
}

4.遍历

二叉搜索树的遍历非常简单,就是之前学习过的二叉树的中序遍历

void InOrder()
{_InOrder(_root);cout << endl;
}void _InOrder(Node* root)
{if (root == nullptr)return;_InOrder(root->_left);cout << root->_key << ' ';_InOrder(root->_right);
}

注:由于调用函数时C++封装的特性,需设计两个函数,InOrder接口对外提供,_InOrder不对外提供。

5.拷贝构造与析构

	//采用前序遍历拷贝构造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;}//采用后序遍历的方式来删除,从下往上删。void _Destroy(Node*& root){if (root == nullptr){return;}_Destroy(root->_left);_Destroy(root->_right);delete root;root = nullptr;}~BSTree(){_Destroy(_root);}

因为拷贝构造二叉搜索树时要保证树的结构与原来树的结构一致,因此采用前序遍历进行拷贝构造。

但如果写了拷贝构造之后编译器就不会生成默认的构造函数了,因为拷贝构造也属于构造,因此可以利用一下C++11的特性,强制编译器生成一个默认的拷贝构造

//强制编译器生成一个默认的拷贝构造
BSTree() = default;

三.二叉搜索树的递归实现

对于搜索二叉树来说,上面实现的非递归版本是比递归版本更优的。此处的递归实现完全属于多余了,但是作为拓展内容看一看也未尝不可。

1.递归查找

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 true;if (root->_key > key)_FindR(root->_left, key);else_FindR(root->_right, key);
}

2.递归插入

bool InsertR(const K& key)
{return _EraseR(_root, key);
}bool _InsertR(Node*& root, const K& key)
{if (root == nullptr){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);elsereturn false;
}

3.递归删除

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;//1.右为空if (root->_right == nullptr)root = root->_left;//2.左为空else if (root->_left)root = root->_right;//3.左右都不为空else{//找左子树最大节点交换Node* maxleft = root->_left;while (maxleft->_right)maxleft = maxleft->_right;//找到后先交换要删除的值与左子树最大节点的值swap(root->_key, maxleft->_key);//再递归到左子树中去删除return _EraseR(root->_left, key);}delete del;return true;}
}

四.二叉搜索树的性能分析

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

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

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

在这里插入图片描述

  • 最优情况下,二叉搜索树为完全二叉树(或者接近完全二叉树),其平均比较次数为: l o g 2 N log_2 N log2N
  • 最差情况下,二叉搜索树退化为单支树(或者类似单支),其平均比较次数为: N 2 \frac{N}{2} 2N

如果退化成单支树,二叉搜索树的性能就失去了。那能否进行改进,不论按照什么次序插 入关键码,二叉搜索树的性能都能达到最优? 那么我们后续章节学习的AVL树和红黑树就可以上场了。

五.二叉树搜索的应用

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

六.源码

namespace dianxia
{template <class K>struct BSTreeNode{BSTreeNode<K>* _left;BSTreeNode<K>* _right;K _key;BSTreeNode(const K& key):_left(nullptr), _right(nullptr), _key(key){}};template <class K>class BSTree{typedef BSTreeNode<K> Node;public://强制编译器生成构造函数BSTree() = default;//拷贝构造BSTree(const BSTree<K>& t){_root = Copy(t._root);}//赋值重载BSTree<k>& operator=(BSTree<K> t){swap(_root, t._root);return *this;}~BSTree(){Destroy(_root);}//插入节点bool Insert(const K& key){//直接插入根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{returen 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{returen 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{//1.左为空if (cur->_left == nullptr){if (cur == _root){_root = cur->_right;}else{if (parent->_left == cur){parent->_left = cur->_right;}else{parent->_right == cur->_right;}}delete cur;}//2.右为空else if (cur->_right == nullptr){if (cur == _root){_root = cur->_left;}else{if (parent->_left == cur){parent->_left = cur->_left;}else{parent->_right == cur->_left;}}delete cur;}//3.左右都不为空,找右树最小节点或左树最大节点替代curelse{Node* pminRight = cur;Node* minRight = cur->_right;while (minRight->_left){pminRight = minRight;minRight = minRight->_left;}cur->_key = minRight->_key;if (pminRight->_left == minRight){pminRight->_left = minRight->_right;}else{pminRight->_right = minRight->_right;}delete minRight}return true;}}return false;}//递归版bool FindR(const K& key){return _FindR(_root, key);}bool InsertR(const K& key){return _InsertR(_root, key);}bool Erase(const K& key){return _Erase(_root, key);}bool Insorder(){_Insorder(_root);cout << endl;}protected:Node* Copy(Node* root){if (root == nullptr)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 == nullptr)return;Destroy(root->_left);Destroy(root->_right);delete root;root = nullptr;}bool _FindR(Node* root, const K& key){if (root == nullptr)return false;if (root->_key == key)return true;if (root->_key < key)return _FindR(root->_right, key);else return _FindR(root->_left, key);}bool _InsertR(Node*& root, const K& key){if (root == nullptr){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{return false;}}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.右为空if (root->_right == nullptr)root = root->_left;//2.左为空else if (root->_left)root = root->_right;//3.左右都不为空else{//找左子树最大节点交换Node* maxleft = root->_left;while (maxleft->_right)maxleft = maxleft->_right;//找到后先交换要删除的值与左子树最大节点的值swap(root->_key, maxleft->_key);//再递归到左子树中去删除return _EraseR(root->_left, key);}delete del;return true;}}private:Node* _root;};
}

本文到此结束,码文不易,还请多多支持!!!

相关文章:

数据结构——搜索二叉树

文章目录 一. 概念二. 二叉搜索树的操作1.查找2.插入3.删除&#xff08;重点&#xff09;4.遍历5.拷贝构造与析构 三.二叉搜索树的递归实现1.递归查找2.递归插入3.递归删除 四.二叉搜索树的性能分析五.二叉树搜索的应用六.源码 前言&#xff1a; 本章我们将认识一种新的二叉树—…...

3.3 Makefile的嵌套包含

一、Makefile包含子Makefile的示例 下面是一个示例Makefile和sub.mk的内容。 首先是sub.mk文件的内容: VAR := Hello from sub.mksay_hello:@echo $(VAR)然后是Makefile文件的内容: include sub.mkall: say_hellosay_hello:@echo "Hello from Makefile"在这个例…...

爬虫获取电影数据----以沈腾参演电影为例

数据可视化&分析实战 1.1 沈腾参演电影数据获取 文章目录 数据可视化&分析实战前言1. 网页分析2. 构建数据获取函数2.1 网页数据获取函数2.2 网页照片获取函数 3. 获取参演影视作品基本数据4. 电影详细数据获取4.1 导演、演员、描述、类型、投票人数、评分信息、电影海…...

网页版Java(Spring/Spring Boot/Spring MVC)五子棋项目(二)前后端实现用户的登录和注册功能【用户模块】

网页版Java五子棋项目&#xff08;二&#xff09;前后端实现用户的登录和注册功能【用户模块】 在用户模块我们要清楚要完成的任务一、MyBatis后端操作数据库1. 需要在数据库创建用户数据库1. 用户id2. 用户名3. 密码4. 天梯积分5. 总场数6. 获胜场数 2. 创建用户类User和数据库…...

2023年华数杯数学建模A题思路代码分析 - 隔热材料的结构优化控制研究

# 1 赛题 A 题 隔热材料的结构优化控制研究 新型隔热材料 A 具有优良的隔热特性&#xff0c;在航天、军工、石化、建筑、交通等 高科技领域中有着广泛的应用。 目前&#xff0c;由单根隔热材料 A 纤维编织成的织物&#xff0c;其热导率可以直接测出&#xff1b;但是 单根隔热…...

阿里云二级域名配置

阿里云二级域名配置 首先需要进入阿里云控制台的域名管理 1.选择域名点击解析 2.添加记录 3.选择A类型 4.主机记录设置【可以aa.bb或者aa.bb.cc】 到时候会变成&#xff1a;aa.bb.***.com 5.解析请求来源设置为默认 6.记录值 设置为要解析的服务器的ip地址 7.TTL 默认即…...

Webpack5 动态导入按需加载

文章目录 一、 什么是动态导入和按需加载&#xff1f;二、 具体用法示例二、 总结 一、 什么是动态导入和按需加载&#xff1f; 传统上&#xff0c;在Webpack中&#xff0c;我们使用import语句可以在代码中静态地导入模块。这意味着所有的模块都会在构建时被打包到bundle中。然…...

【Linux操作系统】Ubuntu和center两个Linux发行版本中指令的区别

Ubuntu和center是Linux的两个发行版本&#xff0c;本文将详细介绍两个发行版的使用命令区别&#xff0c;分析两者的优缺点。 文章目录 常见的区别&#xff1a;细节差异&#xff1a;两个发行版本各自的优点和缺点Ubuntu 的优点&#xff1a;Ubuntu 的缺点&#xff1a;CentOS 的优…...

c++基本数据结构

void insert(const node *head, node *p) {node *x, *y;yhead;do{xy;yx->next;} while ((y!NULL) && (y->value < p->value);x->nextp;p->nexty; } 二.栈 (1) 栈的实现! 操作规则&#xff1a;先进后出&#xff0c;先出后进。 int stack[N], top0; /…...

路由器DHCP实验

拓扑图 配置 # 配置ip地址并开启dhcp [Huawei]int g0/0/0 [Huawei-GigabitEthernet0/0/0]ip addr 192.168.1.1 255.255.255.0 [Huawei-GigabitEthernet0/0/0]dhcp enable## 配置dns地址 [Huawei-GigabitEthernet0/0/0]dhcp dns-list 192.168.1.5## 指定某个接口开通DHCP 功能…...

Linux 电源子系统之充电、放电、低功耗

在嵌入式产品中,有三个重要模块:充电、放电、低功耗。 1、充电 charging 1、开关电源基本原理 2、线性充电和开关电源硬件电路图分析 3、Battery_Charging_v1.2 spec 4、typec spec 5、typec-PD spec 6、Uevent 在 Android 层的实现 7、battery service 监听 uevent 事件以…...

捕捉时刻:将PDF文件中的图像提取为个性化的瑰宝(从pdf提取图像)

应用场景&#xff1a; 该功能的用途是从PDF文件中提取图像。这在以下情况下可能会很有用&#xff1a; 图片提取和转换&#xff1a;可能需要将PDF文件中的图像提取出来&#xff0c;并保存为单独的图像文件&#xff0c;以便在其他应用程序中使用或进行进一步处理。例如&#xff…...

【基础类】—HTTP协议类

一、HTTP协议的主要特点 简单快速&#xff1a;每个资源URI是固定的&#xff0c;访问某个资源输入URI即可灵活&#xff1a;在每一个HTTP协议中&#xff0c;请求头部分有一个数据类型&#xff0c;通过一个HTTP协议可以完成不同的数据类型传输无连接&#xff1a;连接一次就会断开…...

【Qt高级】QThread与QTimer组合使用引出的信号槽执行在哪个线程的思考【2023.08.06】

源码见 testQThread_QTimer… Qt 版本5.6.3 视频讲解&#xff1a;https://www.bilibili.com/video/BV15P411C79i/ 链接: 视频讲解 简介 想法很单纯&#xff0c;就是主线程启动一个子线程&#xff0c;子线程里启动一个定时器&#xff0c;定时执行一些任务&#xff0c;然鹅实际开…...

用于大型图像模型的 CNN 内核的最新内容

一、说明 由于OpenAI的ChatGPT的巨大成功引发了大语言模型的繁荣&#xff0c;许多人预见到大图像模型的下一个突破。在这个领域&#xff0c;可以提示视觉模型分析甚至生成图像和视频&#xff0c;其方式类似于我们目前提示 ChatGPT 的方式。 用于大型图像模型的最新深度学习方法…...

索尼电视怎么完全关机

索尼电视怎么完全关机 当用户想要关闭索尼电视时&#xff0c;可能会遇到一些问题。例如&#xff0c;他们可能会遇到如何完全关闭电视的问题。在本文中&#xff0c;我们将介绍如何完全关闭索尼电视。 首先&#xff0c;您需要找到索尼电视的电源按钮。通常&#xff0c;该按钮位…...

AI介绍——chat gpt/文心一言/claude/bard/星火大模型/bing AI

AI体验 1. AI 介绍&#xff08;注册和使用&#xff09;1.1 Chat GPT1.2 文心一言1.3 Slack 上的 Claude1.3.1 Claude 介绍1.3.2 Claude 使用 1.4 Google的Bard1.4.1 Bard 介绍1.4.2 Bard 使用 1.5 科大讯飞的星火大模型1.5.1 星火大模型 介绍1.5.2 星火大模型 使用 1.6 new bin…...

C++ 访问控制——公有继承、私有继承、保护继承

派生类继承了基类的全部数据成员和除了构造函数和析构函数之外的全部函数成员&#xff0c;但是这些成员的访问属性在派生的过程中是可以调整的。从基类继承的成员&#xff0c;其访问属性由继承方式控制。 基类的成员有public&#xff08;公有&#xff09;、protected&#xff…...

python性能调试

py-spy生成cpu火焰图 ft5.svg env/xxxx/bin pid26443$env/py-spy record -o /tmp/$f --pid $pid --nativememray实时查看内存 env/xxxx/bin$env/python -m memray run --live --trace-python-allocators --native run_demo.pymemray生成内存火焰图报告 frun_demo_042.bin en…...

738. 单调递增的数字

738. 单调递增的数字 当且仅当每个相邻位数上的数字 x 和 y 满足 x < y 时&#xff0c;我们称这个整数是单调递增的。 给定一个整数 n &#xff0c;返回 小于或等于 n 的最大数字&#xff0c;且数字呈 单调递增 。 示例 1: 输入: n 10 输出: 9示例 2: 输入: n 1234 输出…...

[特殊字符] 智能合约中的数据是如何在区块链中保持一致的?

&#x1f9e0; 智能合约中的数据是如何在区块链中保持一致的&#xff1f; 为什么所有区块链节点都能得出相同结果&#xff1f;合约调用这么复杂&#xff0c;状态真能保持一致吗&#xff1f;本篇带你从底层视角理解“状态一致性”的真相。 一、智能合约的数据存储在哪里&#xf…...

TDengine 快速体验(Docker 镜像方式)

简介 TDengine 可以通过安装包、Docker 镜像 及云服务快速体验 TDengine 的功能&#xff0c;本节首先介绍如何通过 Docker 快速体验 TDengine&#xff0c;然后介绍如何在 Docker 环境下体验 TDengine 的写入和查询功能。如果你不熟悉 Docker&#xff0c;请使用 安装包的方式快…...

Golang 面试经典题:map 的 key 可以是什么类型?哪些不可以?

Golang 面试经典题&#xff1a;map 的 key 可以是什么类型&#xff1f;哪些不可以&#xff1f; 在 Golang 的面试中&#xff0c;map 类型的使用是一个常见的考点&#xff0c;其中对 key 类型的合法性 是一道常被提及的基础却很容易被忽视的问题。本文将带你深入理解 Golang 中…...

质量体系的重要

质量体系是为确保产品、服务或过程质量满足规定要求&#xff0c;由相互关联的要素构成的有机整体。其核心内容可归纳为以下五个方面&#xff1a; &#x1f3db;️ 一、组织架构与职责 质量体系明确组织内各部门、岗位的职责与权限&#xff0c;形成层级清晰的管理网络&#xf…...

在鸿蒙HarmonyOS 5中使用DevEco Studio实现录音机应用

1. 项目配置与权限设置 1.1 配置module.json5 {"module": {"requestPermissions": [{"name": "ohos.permission.MICROPHONE","reason": "录音需要麦克风权限"},{"name": "ohos.permission.WRITE…...

ios苹果系统,js 滑动屏幕、锚定无效

现象&#xff1a;window.addEventListener监听touch无效&#xff0c;划不动屏幕&#xff0c;但是代码逻辑都有执行到。 scrollIntoView也无效。 原因&#xff1a;这是因为 iOS 的触摸事件处理机制和 touch-action: none 的设置有关。ios有太多得交互动作&#xff0c;从而会影响…...

安宝特方案丨船舶智造的“AR+AI+作业标准化管理解决方案”(装配)

船舶制造装配管理现状&#xff1a;装配工作依赖人工经验&#xff0c;装配工人凭借长期实践积累的操作技巧完成零部件组装。企业通常制定了装配作业指导书&#xff0c;但在实际执行中&#xff0c;工人对指导书的理解和遵循程度参差不齐。 船舶装配过程中的挑战与需求 挑战 (1…...

无人机侦测与反制技术的进展与应用

国家电网无人机侦测与反制技术的进展与应用 引言 随着无人机&#xff08;无人驾驶飞行器&#xff0c;UAV&#xff09;技术的快速发展&#xff0c;其在商业、娱乐和军事领域的广泛应用带来了新的安全挑战。特别是对于关键基础设施如电力系统&#xff0c;无人机的“黑飞”&…...

day36-多路IO复用

一、基本概念 &#xff08;服务器多客户端模型&#xff09; 定义&#xff1a;单线程或单进程同时监测若干个文件描述符是否可以执行IO操作的能力 作用&#xff1a;应用程序通常需要处理来自多条事件流中的事件&#xff0c;比如我现在用的电脑&#xff0c;需要同时处理键盘鼠标…...

【Linux手册】探秘系统世界:从用户交互到硬件底层的全链路工作之旅

目录 前言 操作系统与驱动程序 是什么&#xff0c;为什么 怎么做 system call 用户操作接口 总结 前言 日常生活中&#xff0c;我们在使用电子设备时&#xff0c;我们所输入执行的每一条指令最终大多都会作用到硬件上&#xff0c;比如下载一款软件最终会下载到硬盘上&am…...