【数据结构进阶】手撕红黑树

🔥个人主页: Forcible Bug Maker
🔥专栏: C++ || 数据结构
目录
- 🌈前言
- 🔥红黑树的概念
- 🔥手撕红黑树
- 红黑树结点的定义
- 红黑树主体需要实现的成员函数
- ==红黑树的插入==
- ==find==
- ==Empty和Size==
- ==拷贝构造==
- ==析构函数和clear==
- ==检测是否为合法红黑树==
- ==Begin和End==
- 红黑树的迭代器接口
- ==* 解引用和 -> 访问==
- ==operator++()==
- ==operator- - ()==
- ==迭代器比较相等==
- 🌈结语
🌈前言
本篇博客主要内容:红黑树的介绍,以及底层代码逻辑和实现。
刚刚接触编程的时候就听说有的大厂HR会让手撕红黑树。心中一直对这个数据结构保持着敬畏和向往,今天在将其撕出来后,用本篇博客加以记录,望周知。
🔥红黑树的概念
红黑树,也是一种二叉搜索树,但再每个结点上增加一个存储位置表示结点的颜色,可以是RED(红)或BLACK(黑)。通过对任何一条根到叶子的路径上各个结点的着色方式的限制,红黑树确保没有一条路径会比其他路径长出两倍,因而是接近平衡的。

一颗红黑树,是具有如下性质的二叉搜索树:
- 每个结点不是红色就是黑色
- 根结点是黑色的
- 如果一个结点是红色,则它的两个孩子结点是黑色(即不会有连续的红结点)
- 对于每个结点,从该结点到其所有后代叶结点的简单路径上,均包含相同数目的黑色结点
- 每个叶子结点都是黑色的(此处的叶子结点指的是空结点NIL)
其中,3和4是最重要的两点。
🔥手撕红黑树
红黑树结点的定义
红黑树的结点包含四个成员变量,模板类型T:可以存储K或者pair<K,V>类型,便于后期封装;三个指针:分别为指向左孩子结点的指针,指向右孩子结点的指针,指向父结点的指针;最后变量_col:枚举类型,可以存RED和BLACK。
enum Color
{RED,BLACK
};template<class T>
struct RBTreeNode
{RBTreeNode<T>(const T& t): _data(t), _left(nullptr), _right(nullptr), _parent(nullptr), _col(RED){}T _data;RBTreeNode<T>* _left;RBTreeNode<T>* _right;RBTreeNode<T>* _parent;Color _col;
};
红黑树主体需要实现的成员函数
// T: 可能是键值对<key,value>
// 可能是一个key
// 不论节点中存储的是<key, value> || key, 都是按照key来进行比较的
// KeyOfValue: 提取data中的Key
template<class K, class T, class KeyOfValue>
class RBTree
{typedef RBTreeNode<T> Node;
public:typedef RBTreeIterator<T, T&, T*> iterator;typedef RBTreeIterator<T, const T&, const T*> const_iterator;
public:RBTree() = default;RBTree(const RBTree<K, T, KeyOfValue>& t);// 插入值为data的节点// 返回值含义:iterator代表新插入节点 bool:代表释放插入成功std::pair<iterator, bool> Insert(const T& data);// Begin和End迭代器iterator Begin();iterator End();// 红黑树是否为空,是返回true,否则返回falsebool Empty()const;// 返回红黑树中有效节点的个数size_t Size()const;// 将红黑树中的有效节点删除void Clear();// 检测红黑树是否为有效的红黑树,注意:其内部主要依靠_IsValidRBTRee函数检测bool IsValidRBTRee()// 在红黑树中查找data,存在赶回该节点对应的迭代器,否则返回End()iterator Find(const T& data);~RBTree();
private:Node* _root;
};
红黑树的插入
红黑树是在二叉搜索树的基础上加上其平衡限制条件,因此红黑树的插入可分为两步:
- 首先是按照二叉搜索树的方式插入结点
- 检测插入结点后,红黑树的性质是否遭到破坏
因为新结点的默认颜色是红色,因此:如果父结点的颜色是黑色,就没有违反性质,不需调整;担当插入结点的父节点也是红色时,就违反了性质3(不能有连在一起的红结点),此时需要对红黑树分情况讨论调整:
约定:cur为当前结点,p为父节点,g为祖父结点,u为叔叔结点。
- 情况一:cur为红,p为红,g为黑,u存在且为红

解决方式:将p,u变成黑色,g变成红色,然后把g改成cur,继续向上调整。
- 情况二:cur为红,p为红,g为黑,u不存在/u存在且为黑(这里需要做的其实就和AVL树中的单旋很像了,我们需要把u结点旋转下来,以维持平衡)

p为g的左孩子,cur为p的左孩子,进行右单旋;相反,p为g的右孩子,cur为p的右孩子,则进行左单旋。p变黑色,g变红色。
- 情况三(情况二的变体):cur为红,p为红,g为黑,u不存在/u存在且为黑(其实就是双旋,除了不用调整平衡因子,其他的和AVL树的双旋并无差别)

p为g的左孩子,cur为p的右孩子,则针对p做左单旋转;相反,p为g的右孩子,cur为p的左孩子,则针对p做右单旋转。然后就转换成了情况2。
针对每种情况进行相应的处理即可:
// 插入值为data的节点
// 返回值含义:iterator代表新插入节点 bool:代表释放插入成功
std::pair<iterator, bool> Insert(const T& data)
{if (_root == nullptr) {_root = new Node(data);_root->_col = BLACK;return std::make_pair(iterator(_root, _root), true);}KeyOfValue kov;Node* parent = nullptr;Node* cur = _root;while (cur) {if (kov(cur->_data) < kov(data)) {parent = cur;cur = cur->_right;}else if (kov(cur->_data) > kov(data)) {parent = cur;cur = cur->_left;}else return std::make_pair(iterator(cur, _root), false);}cur = new Node(data);Node* InsertNode = cur;if (kov(parent->_data) < kov(data)) {parent->_right = cur;cur->_parent = parent;}else {parent->_left = cur;cur->_parent = parent;}while (parent && parent->_col == RED && cur->_col == RED) {Node* grandParent = parent->_parent;Node* uncle = nullptr;// g// p uif (grandParent->_left == parent) {uncle = grandParent->_right;if (uncle == nullptr || uncle->_col == BLACK) {if (parent->_left == cur) {RotateR(grandParent);parent->_col = BLACK;grandParent->_col = RED;}else {RotateL(parent);RotateR(grandParent);cur->_col = BLACK;grandParent->_col = RED;}break;}else {parent->_col = BLACK;grandParent->_col = RED;uncle->_col = BLACK;cur = grandParent;parent = grandParent->_parent;}}// g// u pelse {uncle = grandParent->_left;if (uncle == nullptr || uncle->_col == BLACK) {if (parent->_right == cur) {RotateL(grandParent);parent->_col = BLACK;grandParent->_col = RED;}else {RotateR(parent);RotateL(grandParent);cur->_col = BLACK;grandParent->_col = RED;}break;}else {parent->_col = BLACK;grandParent->_col = RED;uncle->_col = BLACK;cur = grandParent;parent = grandParent->_parent;}}}_root->_col = BLACK;return std::make_pair(iterator(InsertNode, _root), true);
}
在红黑树中,由于不用调节平衡因子,双旋的复杂度大大降低,直接使用单旋并在插入过程中调整结点颜色即可。
旋转的具体内容在AVL树中(【数据结构进阶】AVL树)讲解过,这里就不赘述了。
// 左单旋
void RotateL(Node* parent)
{Node* subR = parent->_right;Node* subRL = subR->_left;Node* parentParent = parent->_parent;if (subRL)subRL->_parent = parent;subR->_left = parent;subR->_parent = parentParent;parent->_right = subRL;parent->_parent = subR;if (parentParent == nullptr) {_root = subR;}else {if (parentParent->_left == parent) {parentParent->_left = subR;}elseparentParent->_right = subR;}
}// 右单旋
void RotateR(Node* parent)
{Node* subL = parent->_left;Node* subLR = subL->_right;Node* parentParent = parent->_parent;if (subLR)subLR->_parent = parent;subL->_right = parent;subL->_parent = parentParent;parent->_left = subLR;parent->_parent = subL;if (parentParent == nullptr) {_root = subL;}else {if (parentParent->_left == parent) {parentParent->_left = subL;}elseparentParent->_right = subL;}
}
find
和二叉搜索树的查找规则相同。
// 在红黑树中查找data,存在赶回该节点对应的迭代器,否则返回End()
iterator Find(const T& data)
{KeyOfValue kov;Node* cur = _root;while (cur) {if (kov(cur->_data) < kov(data))cur = cur->_right;else if (kov(cur->_data) > kov(data))cur = cur->_left;else return iterator(cur, _root);}return iterator(nullptr, _root);
}
Empty和Size
Empty接口函数用来判断树是否为空;Size用来计算返回树结点的个数。
// 红黑树是否为空,是返回true,否则返回false
bool Empty()const
{return _root == nullptr;
}
// 返回红黑树中有效节点的个数
size_t Size()const
{return _Size(_root);
}size_t _Size(Node* root)
{return root == nullptr ? 0 : _Size(root->_left) + _Size(root->_right) + 1;
}
拷贝构造
RBTree(const RBTree<K, T, KeyOfValue>& t)
{_root = _Copy(t._root);
}Node* _Copy(Node* root)
{if (root == nullptr)return nullptr;Node* newRoot = new Node(root->_data);newRoot->_left = _Copy(root->_left);newRoot->_right = _Copy(root->_right);return newRoot;
}
析构函数和clear
// 将红黑树中的有效节点删除
void Clear()
{_Destroy(_root);_root = nullptr;
}~RBTree()
{_Destroy(_root);_root = nullptr;
}void _Destroy(Node* root)
{if (root == nullptr)return;_Destroy(root->_left);_Destroy(root->_right);delete root;
}
检测是否为合法红黑树
在IsValidRBTRee中,首先算出一条路径上的黑结点个数digit_black,然后在每条路径递归到空结点时判断黑结点个数是否相等,即可验证性质4(所有路径上黑结点个数相等);递归的过程中,判断当前结点和父节点的颜色是否同时为红,即可验证性质3(没有连续的红结点)
// 检测红黑树是否为有效的红黑树,注意:其内部主要依靠_IsValidRBTRee函数检测
bool IsValidRBTRee()
{if (_root == nullptr)return true;Node* cur = _root;size_t digit_black = 0;while (cur) {if (cur->_col == BLACK)++digit_black;cur = cur->_left;}return _IsValidRBTRee(_root, digit_black, 0);
}bool _IsValidRBTRee(Node* pRoot, size_t blackCount, size_t pathBlack)
{if (pRoot == nullptr) {if (blackCount == pathBlack)return true;else return false;}if (pRoot->_col == RED && pRoot->_parent->_col == RED) {return false;}if (pRoot->_col == BLACK) {return _IsValidRBTRee(pRoot->_left, blackCount, pathBlack + 1)&& _IsValidRBTRee(pRoot->_right, blackCount, pathBlack + 1);}else {return _IsValidRBTRee(pRoot->_left, blackCount, pathBlack)&& _IsValidRBTRee(pRoot->_right, blackCount, pathBlack);}
}
Begin和End
这两个函数用于获取红黑树的头对象和尾迭代器。
// Begin和End迭代器
iterator Begin()
{return iterator(_LeftMost(), _root);
}
iterator End()
{return iterator(nullptr, _root);
}// 获取红黑树最左侧节点
Node* _LeftMost()
{if (_root == nullptr)return nullptr;Node* parent = _root;Node* cur = parent->_left;while (cur) {parent = cur;cur = cur->_left;}return parent;
}
红黑树的迭代器接口
迭代器的好处是可以方便遍历,使数据结构的底层实现变的透明,从而降低代码编写的复杂程度。
template<class T, class Ref, class Ptr>
struct RBTreeIterator
{typedef RBTreeNode<T> Node;typedef RBTreeIterator<T, Ref, Ptr> Self;RBTreeIterator(Node* pNode,Node* root): _pNode(pNode),_root(root){}// 让迭代器具有类似指针的行为Ref operator*();Ptr operator->();// 然迭代器可以移动:前置/后置++ Self& operator++();Self operator++(int);// 然迭代器可以移动:前置/后置-- Self& operator--();Self operator--(int);// 让迭代器可以比较bool operator!=(const Self& s)const;bool operator==(const Self& s)const;private:Node* _pNode;Node* _root;
};
* 解引用和 -> 访问
// 让迭代器具有类似指针的行为
Ref operator*()
{return _pNode->_data;
}
Ptr operator->()
{return &(_pNode->_data);
}
operator++()
二叉树的中序遍历并不难实现,但是要实现从任意一个结点按中序遍历跑到下一个结点,这就有相当难度了。
具体逻辑为:
- 右子树不为空,访问右子树的最左结点。
- 右子树为空(代表当前结点所在子树访问完了),沿着到根节点的路线,孩子是父亲左的那个祖先结点,就是下一个要访问的结点。
// 迭代器可以移动:前置/后置++
Self& operator++()
{if (_pNode->_right) {_pNode = _pNode->_right;while (_pNode->_left) _pNode = _pNode->_left;}else {Node* cur = _pNode;Node* parent = cur->_parent;while (parent && cur == parent->_right) {cur = parent;parent = parent->_parent;}_pNode = parent;}return *this;
}Self operator++(int)
{Node* rem = _pNode;if (_pNode->_right) {_pNode = _pNode->_right;while (_pNode->_left)_pNode = _pNode->_left;}else {Node* cur = _pNode;Node* parent = cur->_parent;while (parent && cur == parent->_right) {cur = parent;parent = parent->_parent;}_pNode = parent;}return Self(rem);
}
operator- - ()
这时候要判断当前迭代器是否指向尾End(),同时判断树是否为空,这就要用到传入迭代器对象中的_root了。在找到End()的前一个结点之后,按照和operator++()相反的逻辑即可实现operator--()。
具体逻辑为:
- 左子树不为空,访问左子树的最右结点。
- 左子树为空(代表当前结点所在子树访问完了),沿着到根结点的路线,孩子是父亲右的那个祖先结点,就是下一个要访问的结点。
// 迭代器可以移动:前置/后置--
Self& operator--()
{if (_pNode == nullptr) {if (_root == nullptr)assert(false);Node* parent = _root;Node* cur = parent->_right;while (cur) {parent = cur;cur = cur->_right;}_pNode = parent;return *this;}if (_pNode->_left) {_pNode = _pNode->_left;while (_pNode->_right)_pNode = _pNode->_right;}else {Node* cur = _pNode;Node* parent = cur->_parent;while (parent && cur == parent->_left) {cur = parent;parent->_parent;}_pNode = parent;}return *this;
}Self operator--(int)
{if (_pNode == nullptr) {if (_root == nullptr)assert(false);Node* parent = _root;Node* cur = parent->_right;while (cur) {parent = cur;cur = cur->_right;}_pNode = parent;return Self(nullptr);}Node* rem = _pNode;if (_pNode->_left) {_pNode = _pNode->_left;while (_pNode->_right)_pNode = _pNode->_right;}else {Node* cur = _pNode;Node* parent = cur->_parent;while (parent && cur == parent->_left) {cur = parent;parent->_parent;}_pNode = parent;}return Self(rem);
}
迭代器比较相等
底层就是用指针作比较。
// 让迭代器可以比较
bool operator!=(const Self& s)const
{return _pNode != s._pNode;
}bool operator==(const Self& s)const
{return _pNode == s._pNode;
}
🌈结语
红黑树和AVL树都是高效的平衡二叉树,增删改查的时间复杂度都是O( l o g 2 N log_2 N log2N),红黑树不追求绝对平衡,其只需保证最长路径不超过最短路径的2倍,相对而言,降低了插入和旋转的次数,所以在经常进行增删的结构中性能比AVL树更优,而且红黑树实现比较简单,所以实际运用中红黑树更多。
红黑树的底层实现到这里就要结束了,本篇的数据结构较为复杂,模板的使用也有很多容易出错的点,需要多加体会。感谢大家的支持♥
相关文章:
【数据结构进阶】手撕红黑树
🔥个人主页: Forcible Bug Maker 🔥专栏: C || 数据结构 目录 🌈前言🔥红黑树的概念🔥手撕红黑树红黑树结点的定义红黑树主体需要实现的成员函数红黑树的插入findEmpty和Size拷贝构造析构函数和…...
【C++从小白到大牛】类和对象
目录 一、面向过程和面向对象初步认识 二、类的引入 三、类的定义 类的成员函数两种定义方式: 1. 声明和定义全部放在类体中 2. 类声明放在.h文件中,成员函数定义放在.cpp文件中 成员变量命名规则的建议: 四、类的访问限定符 【访问限…...
Kafka 为什么这么快的七大秘诀,涨知识了
我们都知道 Kafka 是基于磁盘进行存储的,但 Kafka 官方又称其具有高性能、高吞吐、低延时的特点,其吞吐量动辄几十上百万。 在座的靓仔和靓女们是不是有点困惑了,一般认为在磁盘上读写数据是会降低性能的,因为寻址会比较消耗时间。…...
一文解决3D车道线检测:最新单目3D车道线检测综述
前言 场景理解是自动驾驶中极具挑战的任务,尤其是车道检测。车道是道路分割的关键,对车辆安全高效行驶至关重要。车道检测技术能自动识别道路标记,对自动驾驶车辆至关重要,缺乏这项技术可能导致交通问题和事故。车道检测面临多种…...
稳中向好,今年新招6000人
团子校招 近日,美团宣布开启面向 2025 届的校园招聘,招聘规模达 6000 人。 虽然相比京东(宣布招聘 16000 人)稍有逊色,但 6000 这个校招规模可一点不少。 要知道,京东是重自营的传统电商,16000 …...
使用kettle开源工具进行跨库数据同步
数据库同步可以用: 1、Navicat 2、Kettle 3、自己写代码 调用码神工具跨库数据同步 -连接 4、其它 实现 这里使用Kettle来同步,主要是开源的,通过配置就可以实现了 Kettle的图形化界面(Spoon)安装参考方法 ht…...
Golang | Leetcode Golang题解之第307题区域和检索-数组可修改
题目: 题解: type NumArray struct {nums, tree []int }func Constructor(nums []int) NumArray {tree : make([]int, len(nums)1)na : NumArray{nums, tree}for i, num : range nums {na.add(i1, num)}return na }func (na *NumArray) add(index, val …...
Golang | Leetcode Golang题解之第301题删除无效的括号
题目: 题解: func checkValid(str string, lmask, rmask int, left, right []int) bool {cnt : 0pos1, pos2 : 0, 0for i : range str {if pos1 < len(left) && i left[pos1] {if lmask>>pos1&1 0 {cnt}pos1} else if pos2 <…...
【Story】《程序员面试的“八股文”辩论:技术基础与实际能力的博弈》
目录 程序员面试中的“八股文”:助力还是阻力?1. “八股文”的背景与定义1.1 “八股文”的起源1.2 “八股文”的常见类型 2. “八股文”的作用分析2.1 理论基础的评价2.1.1 助力2.1.2 阻力 3. 实际工作能力的考察3.1 助力3.2 阻力 4. 面试中的背题能力4.…...
初步了解泛型
目录 泛型的引入 泛型 泛型 泛型类 泛型的上界 泛型的引入 之前学习的数组里面是存放着整型或者自字符串中一种的数组,如果想要在一个数组里面放多种类型数据,我们该怎么去做呢?Object类或许是一个好的解决方法,因为Object类…...
【C#】.net core 6.0 webapi 使用core版本的NPOI的Excel读取数据以及保存数据
欢迎来到《小5讲堂》 这是《C#》系列文章,每篇文章将以博主理解的角度展开讲解。 温馨提示:博主能力有限,理解水平有限,若有不对之处望指正! 目录 背景读取并保存NPOI信息NPOI 插件介绍基本功能示例代码写入 Excel 文件…...
C++推荐的oj网站
洛谷 信息学奥赛一本通 C语言网 codeforces 杭电oj...
springmvc处理http请求的底层逻辑
http-nio-8088-Poller线程中在org.apache.tomcat.util.net.NioEndpoint.Poller#run这个函数里循环检测selector,若发现有SocketEvent.OPEN_READ事件则会将SelectionKey.attachment中的内容作为入参包装成runable,然后由org.apache.tomcat.util.threads.T…...
干货满满,从零到一:编程小白如何在大学成为编程大神?
✨✨ 欢迎大家来访Srlua的博文(づ ̄3 ̄)づ╭❤~✨✨ 🌟🌟 欢迎各位亲爱的读者,感谢你们抽出宝贵的时间来阅读我的文章。 我是Srlua小谢,在这里我会分享我的知识和经验。&am…...
前端-如何通过docker打包Vue服务成镜像并在本地运行(本地可以通过http://localhost:8080/访问前端服务)
1、下载安装docker,最好在vs code里安装docker的插件。 下载链接:https://www.docker.com/products/docker-desktop 🎉 Docker 简介和安装 - Docker 快速入门 - 易文档 (easydoc.net) 2、准备配置文件-dockerfile文件和nginx.conf文件 do…...
零基础学习【Mybatis】这一篇就够了
Mybatis 查询resultType使用resultMap使用单条件查询多条件查询模糊查询返回主键 动态SQLifchoosesetforeachsql片段 配置文件注解增删改查结果映射 查询 resultType使用 当数据库返回的结果集中的字段和实体类中的属性名一一对应时, resultType可以自动将结果封装到实体中 r…...
Shell入门(保姆级教学)
Shell是一种命令行解释器,也是一种脚本语言,广泛应用于Unix和类Unix系统中,例如Linux。它是用户与操作系统内核交互的桥梁,通过Shell可以执行系统命令、管理文件系统、处理文本数据等。本文将带你入门Shell编程,涵盖基…...
【JDK11和JDK8并行与切换】
一、JDK11安装 1、下载jdk11,点击.exe安装在:C:\Program Files\Java\jdk-11\ 2、配置JAVA_HOME 变量名为JAVA_HOME 变量值为jdk安装路径 3、配置PATH 找到系统变量里的PATH 双击或者单击后点击编辑 点击右上角的新建 新建两条 %JAVA_HOME%\bin …...
vue大数据量列表渲染性能优化:虚拟滚动原理
前面咱完成了自定义JuanTree组件各种功能的实现。在数据量很大的情况下,我们讲了两种实现方式来提高渲染性能:前端分页和节点数据懒加载。 前端分页小节:Vue3扁平化Tree组件的前端分页实现 节点数据懒加载小节:Element Tree Plu…...
昇思25天学习打卡营第1天|快速入门
目录 昇思MindSpore介绍MindSpore的API来快速实现一个简单的深度学习模型通过资料更深入的了解昇思MindSpore 昇思MindSpore介绍 今天有幸学习了昇思MindSpore,让我们来简单的了解一下它 昇思MindSpore是一个全场景深度学习框架,旨在实现易开发、高效执行…...
龙虎榜——20250610
上证指数放量收阴线,个股多数下跌,盘中受消息影响大幅波动。 深证指数放量收阴线形成顶分型,指数短线有调整的需求,大概需要一两天。 2025年6月10日龙虎榜行业方向分析 1. 金融科技 代表标的:御银股份、雄帝科技 驱动…...
大数据学习栈记——Neo4j的安装与使用
本文介绍图数据库Neofj的安装与使用,操作系统:Ubuntu24.04,Neofj版本:2025.04.0。 Apt安装 Neofj可以进行官网安装:Neo4j Deployment Center - Graph Database & Analytics 我这里安装是添加软件源的方法 最新版…...
三维GIS开发cesium智慧地铁教程(5)Cesium相机控制
一、环境搭建 <script src"../cesium1.99/Build/Cesium/Cesium.js"></script> <link rel"stylesheet" href"../cesium1.99/Build/Cesium/Widgets/widgets.css"> 关键配置点: 路径验证:确保相对路径.…...
MongoDB学习和应用(高效的非关系型数据库)
一丶 MongoDB简介 对于社交类软件的功能,我们需要对它的功能特点进行分析: 数据量会随着用户数增大而增大读多写少价值较低非好友看不到其动态信息地理位置的查询… 针对以上特点进行分析各大存储工具: mysql:关系型数据库&am…...
安宝特方案丨XRSOP人员作业标准化管理平台:AR智慧点检验收套件
在选煤厂、化工厂、钢铁厂等过程生产型企业,其生产设备的运行效率和非计划停机对工业制造效益有较大影响。 随着企业自动化和智能化建设的推进,需提前预防假检、错检、漏检,推动智慧生产运维系统数据的流动和现场赋能应用。同时,…...
ETLCloud可能遇到的问题有哪些?常见坑位解析
数据集成平台ETLCloud,主要用于支持数据的抽取(Extract)、转换(Transform)和加载(Load)过程。提供了一个简洁直观的界面,以便用户可以在不同的数据源之间轻松地进行数据迁移和转换。…...
Rapidio门铃消息FIFO溢出机制
关于RapidIO门铃消息FIFO的溢出机制及其与中断抖动的关系,以下是深入解析: 门铃FIFO溢出的本质 在RapidIO系统中,门铃消息FIFO是硬件控制器内部的缓冲区,用于临时存储接收到的门铃消息(Doorbell Message)。…...
深度学习习题2
1.如果增加神经网络的宽度,精确度会增加到一个特定阈值后,便开始降低。造成这一现象的可能原因是什么? A、即使增加卷积核的数量,只有少部分的核会被用作预测 B、当卷积核数量增加时,神经网络的预测能力会降低 C、当卷…...
python报错No module named ‘tensorflow.keras‘
是由于不同版本的tensorflow下的keras所在的路径不同,结合所安装的tensorflow的目录结构修改from语句即可。 原语句: from tensorflow.keras.layers import Conv1D, MaxPooling1D, LSTM, Dense 修改后: from tensorflow.python.keras.lay…...
安宝特案例丨Vuzix AR智能眼镜集成专业软件,助力卢森堡医院药房转型,赢得辉瑞创新奖
在Vuzix M400 AR智能眼镜的助力下,卢森堡罗伯特舒曼医院(the Robert Schuman Hospitals, HRS)凭借在无菌制剂生产流程中引入增强现实技术(AR)创新项目,荣获了2024年6月7日由卢森堡医院药剂师协会࿰…...
