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

【数据结构高阶】二叉搜索树

接下来我们来开始使用C++来详细讲解数据结构的一些高阶的知识点

本期讲解的是二叉搜索树,对于初阶二叉树有所遗忘的同学可以看到这里:

【精选】【数据结构初阶】链式二叉树的解析及一些基本操作

讲解二叉搜索树主要是为了后面的map和set做铺垫,废话不多说我们直接上干货:


目录

一、二叉搜索树的概念

二、模拟实现二叉搜索树

2.1 插入数据

2.1.1 插入数据的非递归实现

2.2 遍历数据

2.3 查找数据

2.4 删除数据

2.4.1 删除数据的非递归实现

2.5 模拟实现二叉搜索树的全部代码


一、二叉搜索树的概念

二叉搜索树又称二叉排序树(BST, Binary Search Tree),它可以是一棵空树,或者是具有以下性质的二叉树:

若它的左子树不为空,则左子树上所有节点的值都小于根节点的值

若它的右子树不为空,则右子树上所有节点的值都大于根节点的值

它的左右子树也分别为二叉搜索树

 例如:

 

我们可以发现的一点:无论是什么样的二叉搜索树,使用中序遍历,遍历出的值都是升序排列的

二、模拟实现二叉搜索树

下面又到了我们最激动人心的代码实现环节,本次代码实现我们还是要基于链式二叉树的实现:

template<class K>
struct BSTreeNode//节点
{BSTreeNode<K>* _lchild;BSTreeNode<K>* _rchild;K _key;BSTreeNode(const K& key):_lchild(nullptr),_rchild(nullptr),_key(key){}
};

2.1 插入数据

我们可以根据二叉搜索树的规律来向其中插入数据,但是插入数据时需要注意一点:要记录插入节点的上一个父节点,将插入的节点连接上二叉树:

template<class K>
class BSTree 
{typedef BSTreeNode<K> Node;
public:bool Insert(const K& key){if (_root == nullptr){_root = new Node(key);_root->_key = key;return true;}Node* cur = _root;//使用cur遍历二叉树找到合适的插入位置Node* parent = cur;//记录cur的父节点while (cur){parent = cur;if (key < cur->_key){cur = cur->_lchild;}else if (key > cur->_key){cur = cur->_rchild;}else{return false;//这里创建的二叉搜索树不允许出现值的冗余}}cur = new Node(key);//创建//将创建的节点链接到二叉树上if (key < parent->_key){parent->_lchild = cur;}else{parent->_rchild = cur;}return true;}private:Node* _root = nullptr;//根节点
};

2.1.1 插入数据的非递归实现

递归的效率并没有循环高,那为什么要说一下插入数据的非递归实现呢

主要是非递归的数据插入的传值方法值得一说:

template<class K>
class BSTree 
{typedef BSTreeNode<K> Node;
public:bool InsertR(const K& key)//插入数据(递归){return _InsertR(_root, key);}bool _InsertR(Node*& root,const K& key)//这里使用指针的引用用来直接修改其父节点指针的指向{if (root == nullptr){root = new Node(key);return true;}if (root->_key > key){_InsertR(root->_lchild, key);}else if (root->_key < key){_InsertR(root->_rchild, key);}else{return false;}}
private:Node* _root = nullptr;//根节点
};

我们可以看到在递归时,传入的形参类型为Node*&,这样可以直接在其函数内部习惯其父节点孩子指针的指向

那为什么要写两个插入函数呢?因为如果我们直接使用_InsertR函数,无法直接使用对象对_InsertR函数进行传参

2.2 遍历数据

因为二叉搜索树的性质,这里我们采用中序遍历: 

template<class K>
class BSTree 
{typedef BSTreeNode<K> Node;
public:bool Insert(const K& key)插入数据{....}void InOrder()//中序遍历{_InOrder(_root);cout << endl;}void _InOrder(Node* root){if (root == NULL)//如果是空树就直接结束{return;}_InOrder(root->_lchild);//先递归遍历其左子树cout << root->_key << " ";//再遍历其根节点_InOrder(root->_rchild);//最后递归遍历其右子树}private:Node* _root = nullptr;//根节点
};

那为什么要写两个中序遍历函数呢?因为如果我们直接使用_InOrder函数,无法直接使用对象对_InOrder函数进行传参

2.3 查找数据

template<class K>
class BSTree 
{typedef BSTreeNode<K> Node;
public:bool Insert(const K& key)//插入数据{......}void InOrder()//中序遍历{......}void _InOrder(Node* root){......}bool Find(const K& key)//查找数据{Node* cur = _root;while (cur){if (cur->_key < key){cur = cur->_rchild;}else if (cur->_key > key){cur = cur->_lchild;}else{return true;}}return false;}private:Node* _root = nullptr;//根节点
};

2.4 删除数据

对于在二叉搜索树中删除数据,我们就要好好说道说道了

下面我们有这样的一个二叉搜索树:

现在我们要删除其叶子节点,这很容易,直接删除完,再将其父节点对应的孩子指针置空即可

那我们要删只有一个孩子节点的数据呢?例如14和10

这个稍微麻烦一点,删除完该节点后将其孩子节点托付给其父节点即可:

那要删带有两个孩子节点的数据呢?例如3、6、8:

对于这种情况我们可以选择其节点下的左子树的最大节点(左子树的最右节点),或者右子树的最小节点(右子树的最左节点)来替代要删除的节点:

综上所述,要删除的结点可能分下面四种情况:

a. 要删除的结点无孩子结点

b. 要删除的结点只有左孩子结点 

c. 要删除的结点只有右孩子结点

d. 要删除的结点有左、右孩子结点

看起来有待删除节点有4中情况,实际情况a可以与情况b或者c合并起来,因此真正的删除过程如下:

情况b:删除该结点且使被删除节点的双亲结点指向被删除节点的左孩子结点--直接删除

情况c:删除该结点且使被删除节点的双亲结点指向被删除结点的右孩子结点--直接删除

情况d:在它的右子树中寻找中序下的第一个结点(关键码最小),用它的值填补到被删除节点中,再来处理该结点的删除问题--替换法删除

思路我们有了,下面用代码来实现一下:

template<class K>
class BSTree 
{typedef BSTreeNode<K> Node;
public:bool Insert(const K& key)//插入数据{...}void InOrder()//中序遍历{...}private:void _InOrder(Node* root){...}public:bool Find(const K& key)//查找数据{...}bool Erase(const K& key){Node* cur = _root;//使用cur遍历二叉树找到要删除元素的位置Node* parent = cur;//记录cur的父节点while (cur)//寻找需要删除的节点{if (cur->_key < key){parent = cur;cur = cur->_rchild;}else if (cur->_key > key){parent = cur;cur = cur->_lchild;}else//找到了,开始删除{if (cur->_lchild == cur->_rchild && cur->_lchild == nullptr)//删除的节点为叶子节点{if (parent == cur)//删除的是根节点{_root = nullptr;//更新根节点}//将其父节点指向的自身的指针置空else if (parent->_lchild == cur){parent->_lchild = nullptr;}else{parent->_rchild = nullptr;}//释放节点空间delete cur;cur = nullptr;}else if (cur->_rchild == nullptr)//删除的节点右孩子为空,左孩子不为空{if (parent == cur)//删除的是根节点{_root = cur->_lchild;//更新根节点}//将删除节点的左孩子交给其父节点else if (parent->_lchild == cur){parent->_lchild = cur->_lchild;}else{parent->_rchild = cur->_lchild;}//释放节点空间delete cur;cur = nullptr;}else if (cur->_lchild == nullptr)//删除的节点左孩子为空,右孩子不为空{if (parent == cur)//删除的是根节点{_root = cur->_rchild;//更新根节点}//将删除节点的右孩子交给其父节点else if (parent->_lchild == cur){parent->_lchild = cur->_rchild;}else{parent->_rchild = cur->_rchild;}//释放节点空间delete cur;cur = nullptr;}else//删除的节点左右孩子都不为空,要找到删除节点的左子树的最大节点或右子树的最小节点可将其替换{//这里找要删除节点的左子树的最大节点(右子树的最小节点也可)Node* maxleft = cur->_lchild; // 记录左子树的最大节点Node* pmaxleft = cur;//记录左子树的最大节点的父节点while (maxleft->_rchild)//查找左子树的最大节点{pmaxleft = maxleft;maxleft = maxleft->_rchild;}//将找到的节点替换要删除的节点cur->_key = maxleft->_key;if (pmaxleft->_lchild == maxleft){pmaxleft->_lchild = maxleft->_lchild;}else{pmaxleft->_rchild = maxleft->_lchild;}//释放节点空间delete maxleft;maxleft = nullptr;}return true;}}return false;//没找到要删除的节点}
private:Node* _root = nullptr;//根节点
};

2.4.1 删除数据的非递归实现

template<class K>
class BSTree 
{typedef BSTreeNode<K> Node;
public:bool EraseR(const K& key)//递归删除数据{return _EraseR(_root, key);}bool _EraseR(Node*& root, const K& key){if (root == nullptr){return false;}else if (root->_key < key){_EraseR(root->_rchild, key);}else if (root->_key > key){_EraseR(root->_lchild, key);}else{Node* del = root;if (root->_lchild == root->_rchild && root->_lchild == nullptr)//删除的节点为叶子节点{//释放节点空间delete root;//将其父节点指向的自身的指针置空root = nullptr;}else if (root->_rchild == nullptr)//删除的节点右孩子为空,左孩子不为空{//将删除节点的左孩子交给其父节点root = root->_lchild;//释放节点空间delete del;del = nullptr;}else if (root->_lchild == nullptr)//删除的节点左孩子为空,右孩子不为空{//将删除节点的右孩子交给其父节点root = root->_rchild;//释放节点空间delete del;del = nullptr;}else//删除的节点左右孩子都不为空,要找到删除节点的左子树的最大节点或右子树的最小节点将其替换{//这里找要删除节点的右子树的最小节点(左子树的最大节点也可)Node* minright = del->_rchild; // 记录右子树的最小节点while (minright->_lchild)//查找右子树的最小节点{minright = minright->_lchild;}root->_key = minright->_key;//将找到的节点替换要删除的节点return _EraseR(root->_rchild, root->_key);//继续递归删除其右子树中用来替换的节点}return true;}}private:Node* _root = nullptr;//根节点
};

 

2.5 模拟实现二叉搜索树的全部代码

#include<iostream>
using namespace std;template<class K>
struct BSTreeNode//节点
{BSTreeNode<K>* _lchild;BSTreeNode<K>* _rchild;K _key;BSTreeNode(const K& key):_lchild(nullptr),_rchild(nullptr),_key(key){}
};template<class K>
class BSTree 
{typedef BSTreeNode<K> Node;
public:bool Insert(const K& key)//插入数据{if (_root == nullptr)//如果根节点为空就直接插入{_root = new Node(key);_root->_key = key;return true;}Node* cur = _root;//使用cur遍历二叉树找到合适的插入位置Node* parent = cur;//记录cur的父节点while (cur){parent = cur;if (key < cur->_key){cur = cur->_lchild;}else if (key > cur->_key){cur = cur->_rchild;}else{return false;//这里创建的二叉搜索树不允许出现值的冗余}}cur = new Node(key);//创建//将创建的节点链接到二叉树上if (key < parent->_key){parent->_lchild = cur;}else{parent->_rchild = cur;}return true;}bool InsertR(const K& key)//插入数据(递归){return _InsertR(_root, key);}private:bool _InsertR(Node*& root,const K& key)//这里使用指针的引用用来直接修改其父节点指针的指向{if (root == nullptr){root = new Node(key);return true;}if (root->_key > key){_InsertR(root->_lchild, key);}else if (root->_key < key){_InsertR(root->_rchild, key);}else{return false;}}public:void InOrder()//中序遍历{_InOrder(_root);cout << endl;}private:void _InOrder(Node* root){if (root == NULL)//如果是空树就直接结束{return;}_InOrder(root->_lchild);//先递归遍历其左子树cout << root->_key << " ";//再遍历其根节点_InOrder(root->_rchild);//最后递归遍历其右子树}public:bool Find(const K& key)//查找数据{Node* cur = _root;while (cur){if (cur->_key < key){cur = cur->_rchild;}else if (cur->_key > key){cur = cur->_lchild;}else{return true;}}return false;}bool Erase(const K& key)//删除数据{Node* cur = _root;//使用cur遍历二叉树找到要删除元素的位置Node* parent = cur;//记录cur的父节点while (cur)//寻找需要删除的节点{if (cur->_key < key){parent = cur;cur = cur->_rchild;}else if (cur->_key > key){parent = cur;cur = cur->_lchild;}else//找到了,开始删除{if (cur->_lchild == cur->_rchild && cur->_lchild == nullptr)//删除的节点为叶子节点{if (parent == cur)//删除的是根节点{_root = nullptr;//更新根节点}//将其父节点指向的自身的指针置空else if (parent->_lchild == cur){parent->_lchild = nullptr;}else{parent->_rchild = nullptr;}//释放节点空间delete cur;cur = nullptr;}else if (cur->_rchild == nullptr)//删除的节点右孩子为空,左孩子不为空{if (parent == cur)//删除的是根节点{_root = cur->_lchild;//更新根节点}//将删除节点的左孩子交给其父节点else if (parent->_lchild == cur){parent->_lchild = cur->_lchild;}else{parent->_rchild = cur->_lchild;}//释放节点空间delete cur;cur = nullptr;}else if (cur->_lchild == nullptr)//删除的节点左孩子为空,右孩子不为空{if (parent == cur)//删除的是根节点{_root = cur->_rchild;//更新根节点}//将删除节点的右孩子交给其父节点else if (parent->_lchild == cur){parent->_lchild = cur->_rchild;}else{parent->_rchild = cur->_rchild;}//释放节点空间delete cur;cur = nullptr;}else//删除的节点左右孩子都不为空,要找到删除节点的左子树的最大节点或右子树的最小节点可将其替换{//这里找要删除节点的左子树的最大节点(右子树的最小节点也可)Node* maxleft = cur->_lchild; // 记录左子树的最大节点Node* pmaxleft = cur;//记录左子树的最大节点的父节点while (maxleft->_rchild)//查找左子树的最大节点{pmaxleft = maxleft;maxleft = maxleft->_rchild;}//将找到的节点替换要删除的节点cur->_key = maxleft->_key;if (pmaxleft->_lchild == maxleft){pmaxleft->_lchild = maxleft->_lchild;}else{pmaxleft->_rchild = maxleft->_lchild;}//释放节点空间delete maxleft;maxleft = nullptr;}return true;}}return false;//没找到要删除的节点}bool EraseR(const K& key)//递归删除数据{return _EraseR(_root, key);}private:bool _EraseR(Node*& root, const K& key){if (root == nullptr){return false;}else if (root->_key < key){_EraseR(root->_rchild, key);}else if (root->_key > key){_EraseR(root->_lchild, key);}else{Node* del = root;if (root->_lchild == root->_rchild && root->_lchild == nullptr)//删除的节点为叶子节点{//释放节点空间delete root;//将其父节点指向的自身的指针置空root = nullptr;}else if (root->_rchild == nullptr)//删除的节点右孩子为空,左孩子不为空{//将删除节点的左孩子交给其父节点root = root->_lchild;//释放节点空间delete del;del = nullptr;}else if (root->_lchild == nullptr)//删除的节点左孩子为空,右孩子不为空{//将删除节点的右孩子交给其父节点root = root->_rchild;//释放节点空间delete del;del = nullptr;}else//删除的节点左右孩子都不为空,要找到删除节点的左子树的最大节点或右子树的最小节点将其替换{//这里找要删除节点的右子树的最小节点(左子树的最大节点也可)Node* minright = del->_rchild; // 记录右子树的最小节点while (minright->_lchild)//查找右子树的最小节点{minright = minright->_lchild;}root->_key = minright->_key;//将找到的节点替换要删除的节点return _EraseR(root->_rchild, root->_key);//继续递归删除其右子树中用来替换的节点}return true;}}private:Node* _root = nullptr;//根节点
};

本期博客到这里就结束了,代码量较大,还请各位仔细分析呀

我们下期见~

相关文章:

【数据结构高阶】二叉搜索树

接下来我们来开始使用C来详细讲解数据结构的一些高阶的知识点 本期讲解的是二叉搜索树&#xff0c;对于初阶二叉树有所遗忘的同学可以看到这里&#xff1a; 【精选】【数据结构初阶】链式二叉树的解析及一些基本操作 讲解二叉搜索树主要是为了后面的map和set做铺垫&#xff…...

如何设计短域名系统

输入可能是 一个冗长的域名&#xff0c;过期时间和自定义的别名输出 自定义别名或者随机生成的短域名&#xff0c;在过期时间到来之前访问都可以被重定向到冗长的域名上约束条件 1.过期后就失效 2.短域名是唯一的 3.自定义短域名长度在7个字符&#xff08;不包含域名长度&am…...

web缓存-----squid代理服务

squid相关知识 1 squid的概念 Squid服务器缓存频繁要求网页、媒体文件和其它加速回答时间并减少带宽堵塞的内容。 Squid代理服务器&#xff08;Squid proxy server&#xff09;一般和原始文件一起安装在单独服务器而不是网络服务器上。Squid通过追踪网络中的对象运用起作用。…...

nginx-location和proxy_pass的url拼接

在proxy_pass中端口号后面如果加入了"/",则location 匹配的内容全部去掉; Nginx中proxy_pass末尾带斜杠/和不带的区别 一、proxy_pass末尾有斜杠 location /api/ { proxy_pass http://127.0.0.1:8000/; } 请求地址&#xff1a;http://localhost/api/test 转发地…...

从零开始配置离线服务器

1.复制环境&#xff08;包含torch包&#xff09; 使用conda pack进行环境迁移&#xff08;步骤很详细&#xff09;_小舟%的博客-CSDN博客 注意&#xff1a;用pack的时候会默认把生成的tar.gz保存到当前目录&#xff0c;所以提前需要观测好在哪 注意&#xff1a;公用的环境必…...

Spring事务和事务的传播机制

目录 Spring中事务的实现 MySQL中的事务使用 Spring 编程式事务 TransactionTemplate 编程式事务 TransactionManager编程式事务 Spring声明式事务 Transactional 参数说明 事务因为程序异常捕获不会自动回滚的解决方案 Transactional 原理 Spring 事务隔离级别 Spring…...

软件开发提效工具——低代码(Low-Code)

目录 一、什么是低代码&#xff08;Low-Code&#xff09;&#xff1f; 二、构建轻量化平台 1、软件开发快效率高 2、满足企业的多样化需求 3、轻松与异构系统集成 4、软件维护成本低 5、为企业实现降本增效 三、小结 一、什么是低代码&#xff08;Low-Code&#xff09;&#xf…...

菜单栏管理软件 Bartender 3 mac中文版功能介绍

​Bartender 3 mac是一款菜单栏管理软件&#xff0c;该软件可以将指定的程序图标隐藏起来&#xff0c;需要时呼出即可。 Bartender 3 mac功能介绍 Bartender 3完全支持macOS Sierra和High Sierra。 更新了macOS High Sierra的用户界面 酒吧现在显示在菜单栏中&#xff0c;使其…...

ef core code first pgsql

在使用efcode来操作pgsql的时候&#xff0c;总有些基础配置流程项目建立完之后后面就很少用&#xff0c;总是忘掉&#xff0c;写个文档记忆一下吧。基于net 6.0。 1.创建一个mvc项目和一个EF类库 2.在类库里面安装依赖dll Microsoft.EntityFrameworkCore.Design 需要添加的…...

容器化nacos部署并实现服务发现(gradle)

1.如何容器化部署mysql 2. 如何容器化部署nacos 为不暴露我的服务器地址&#xff0c;本文全部使用localhost来代替服务器地址&#xff0c;所有的localhost都应该调整为你自己的服务器地址。 为不暴露我的服务器地址&#xff0c;本文全部使用localhost来代替服务器地址&#x…...

金融行业如何数字化转型?_光点科技

金融行业的数字化转型涉及技术创新的引入、客户体验的改善、内部流程的优化、安全和合规性的加强以及员工技能和企业文化的转变。 技术创新 包括云计算、人工智能、大数据分析和区块链技术的采用。云计算增强数据处理的灵活性&#xff0c;AI和机器学习在风险评估和欺诈检测方面…...

【LeetCode刷题-滑动窗口】--1695.删除子数组的最大得分

1695.删除子数组的最大得分 注意&#xff1a;子数组为不同元素 方法&#xff1a;滑动窗口 使用变长滑动窗口寻找数组nums中的以每个下标作为结束下标的元素各不相同的最长子数组。用[start,end]表示滑动窗口&#xff0c;初始时startend0&#xff0c;将滑动窗口的右端点end向右…...

iOS OpenGL ES3.0入门实践

一、效果图 入门实践&#xff0c;做的东西比较简单&#xff0c;效果如下&#xff1a; 二、关于顶点坐标和纹理坐标 绘制图片需要设置顶点坐标和纹理坐标并加载像素数据&#xff0c;之所以要指定两组坐标是因为纹理和顶点使用不同的坐标系&#xff0c;就是告诉OpenGL&#xf…...

网络基础(一)

文章目录&#xff1a; 计算机网络认识计算机网络背景网络发展认识 “协议” 网络协议初识协议分层OSI七层模型TC/IP 五层&#xff08;或四层&#xff09;模型 网络传输基本流程网络传输流程图同局域网的两台主机进行通信跨网络的两台主机进行通信数据包的封装和分用 网络中的地…...

SQLServer添加Oracle链接服务器

又一次在项目中用到了在SQLServer添加Oracle链接服务器&#xff0c;发现之前文章写的也不太好使&#xff0c;那就再总结一次吧。 1、安装OracleClient 安装64位&#xff0c;多数SQLServer是64位&#xff0c;所以OracleClient也安装64位的&#xff1b; 再一个一般安装的Oracl…...

2017年计网408

第33题 假设 OSI 参考模型的应用层欲发送 400B 的数据 (无拆分), 除物理层和应用层之外, 其他各层在封装 PDU 时均引入 20 B 的额外开销, 则应用层数据传输效率约为( )A. 80%B. 83%C. 87%D. 91% 本题考察有关数据包逐层封装的相关概念。我们来一起分析一下。 这是要求大家必须…...

UE5中APlayerController属性与方法列表(翻译中......)

一、属性列表 类型 名称 描述 TObjectPtr< APa... AcknowledgedPawn 用于网络游戏&#xff0c;使客户端可以承认它拥有一个特定的卒。 TArray< FActive... ActiveForceFeedbackEffects TSharedPtr< str... ActiveHapticEffect_Gun TSharedPtr< str..…...

TCP连接保活机制

在TCP连接中有一个保活机制&#xff0c;叫做Keep-Alive&#xff0c;用语言描述就是如下&#xff1a; 在保活时间内&#xff0c;如果没有任何连接相关的活动&#xff0c;TCP 保活机制会开始作用&#xff0c;每隔一个时间间隔&#xff08;保活时间间隔&#xff09;&#xff0c;发…...

centos安装mysql8.0.20、tar包安装方式

基础环境centos7.5&#xff0c;mysql版本8.0.20&#xff0c;通过tar包安装&#xff0c;安装路径/usr/local。 mysql官网&#xff1a;https://dev.mysql.com/downloads/mysql/ wget https://dev.mysql.com/get/Downloads/MySQL-8.0/mysql-8.0.20-linux-glibc2.12-x86_64.tar.x…...

k8s yaml文件含义

文章目录 1、YAML的文件格式和注意事项2、YAML各个字段含义 1、YAML的文件格式和注意事项 不支持制表符tab键缩进&#xff0c;需要使用空格缩进&#xff0c;使用缩进表示层级关系通常开头缩进2个空格&#xff0c;缩进的空格数不重要&#xff0c;只要相同层级的元素左对齐即可字…...

ProEasy机器人:运动+通讯相关说明

----------------机械手运动------- --常用指令&#xff1a;MovP弧线运动、MovL直线运动 MovP(1) --弧线轨迹运动到一号点 MovP(2) --弧线轨迹运动到二号点 MovL(1) --直线轨迹运动到一号点 MovL(2) --直线轨迹运…...

Visual Studio Code 配置 C/C++ 开发环境的最佳实践(VSCode + Clangd + CMake)

Visual Studio Code 配置 C/C 开发环境的最佳实践(VSCode Clangd CMake) 知乎原文参考地址1 与 VSCode 官方文档配置 相比拥有的优势 Clangd 具有更优秀的性能&#xff0c;微软官方 Cpptools 的代码提示功能有明显延迟Clangd 提供更精准的「转到定义」、「重命名符号」、「…...

年产200万件的超级工厂投产!巨头「闭环」汽车电子全产业链

随着汽车电动化程度的提升&#xff0c;汽车电子部件占整车成本比重逐步升高&#xff0c;已经从2012年的25%上升到2021年的55%。 且汽车电子电气架构向整车集中演进&#xff0c;智能汽车已经进入了软件及数据定义时代&#xff0c;底层硬件打破了过去几十年围绕特定应用不断增加…...

智能穿戴AR眼镜主板方案定制_MTK平台AR智能眼镜PCB板开发

AR智能眼镜&#xff0c;是采用了多种技术实现增强现实效果&#xff0c;是将虚拟信息和现实场景相结合的智能设备。 AR智能眼镜硬件上&#xff0c;包括多个传感器、显示装置和处理器等。其中&#xff0c;传感器用于捕捉用户的动作和环境信息&#xff0c;如摄像头、陀螺仪、加速…...

【入门Flink】- 11Flink实现动态TopN

基本处理函数&#xff08;ProcessFunction&#xff09; stream.process(new MyProcessFunction())方法需要传入一个 ProcessFunction 作为参数&#xff0c;ProcessFunction 不是接口 &#xff0c; 而是一个抽象类 &#xff0c;继承了AbstractRichFunction&#xff0c;所有的处…...

现在做跨境电商还需要全球代理IP吗?全球代理IP哪家靠谱?

随着全球互联网的发展&#xff0c;电商平台的发展和跨境贸易的便利化&#xff0c;跨境电商在过去几年中也一直呈现增长趋势&#xff0c;吸引了越来越多的企业和个体创业者入行。 然而&#xff0c;行业竞争也在不断加剧&#xff0c;跨境电商面临更多的越来越多的挑战&#xff0…...

微服务的常用组件

1、注册中心组件 Eureka、Nacos、Zookeeper、Consul 2、负载均衡组件 Ribbon 3、远程调用组件 OpenFeign 4、网关组件 Zuul、Gateway 5、服务保护组件 Hystrix、Sentinel 6、服务配置管理组件 SpringCloudConfig、Nacos、Apollo&#xff08;阿波罗&#xff09;配置中…...

Android问题笔记四十五:解决SeekBar操作thumb周围产生的圆形阴影/灰色阴影/白色圆圈的解决办法

点击跳转专栏>Unity3D特效百例点击跳转专栏>案例项目实战源码点击跳转专栏>游戏脚本-辅助自动化点击跳转专栏>Android控件全解手册点击跳转专栏>Scratch编程案例点击跳转>软考全系列点击跳转>蓝桥系列点击跳转>ChatGPT和AIGC &#x1f449;关于作者 专…...

AI从入门到精通,什么是LLMs大型语言模型?

大型语言模型是指由大量文本数据训练而成的自然语言处理模型&#xff0c;它可以生成高质量的文本内容&#xff0c;并能够理解和回答人类提出的问题。 这种模型通常基于深度学习技术&#xff0c;如循环神经网络&#xff08;RNN&#xff09;或变换器&#xff08;Transformer&…...

优测云测试平台 | 有效的单元测试(下)

接着上一篇内容&#xff0c;我们继续~ 四、测试的目标之三&#xff1a;快速反馈 测试的快速反馈有两个方面的含义: 1.测试运行要快速出结果。 2.当测试失败时&#xff0c;要能快速定位失败原因。 测试运行效率决定了开发的工作周期运转的快慢。在理想的 TDD 模型中&#x…...