C++ STL set与multiset容器:红黑树实现、核心操作与性能优化指南

news2026/5/21 9:15:15
1. 容器概览为什么我们需要 set 和 multiset在C的日常开发里尤其是处理需要快速查找、去重或排序的数据集合时std::set和std::multiset这两个关联容器出场率极高。很多刚从顺序容器如vector、list转过来的朋友一开始可能会觉得它们有点“神秘”——底层是红黑树、元素自动排序、查找效率是O(log n)。但当你真正理解其设计哲学和应用场景后会发现它们简直是解决特定问题的“瑞士军刀”。简单来说set是一个有序的、元素唯一的集合。你扔进去一堆数比如{5, 2, 8, 2, 9}它内部会自动排序并去重最终存储为{2, 5, 8, 9}。而multiset则允许元素重复同样保持有序性输入{5, 2, 8, 2, 9}会得到{2, 2, 5, 8, 9}。它们的核心价值在于提供了基于关键字的快速检索能力这种“快速”不是线性的而是对数级别的这对于数据量稍大的场景至关重要。想象一下这样的需求你需要维护一个实时更新的用户ID列表并频繁地检查某个ID是否存在、插入新ID或删除旧ID。如果使用vector每次查找都要遍历用户量上万时性能就会捉襟见肘。而使用set这些操作都能在对数时间内完成用户体验丝滑流畅。再比如需要统计一篇文章中所有单词的出现频率并按照字母顺序输出multiset就能天然地存储重复的单词并保持有序为后续处理提供便利。2. 核心特性与底层实现深度解析2.1 有序性与唯一性的本质set/multiset的有序性并非一个简单的“功能”而是其底层数据结构的必然结果。C标准规定它们通常基于红黑树一种自平衡的二叉搜索树实现。红黑树通过在插入和删除时进行特定的旋转和变色操作确保树始终保持大致平衡从而保证了最坏情况下的查找、插入、删除时间复杂度均为O(log n)。这种有序性是“严格弱序”的意味着容器内的元素总是按照你指定的比较规则默认为std::less即升序排列好。唯一性针对set则是在此有序结构上实现的自然约束。当尝试插入一个元素时红黑树会从根节点开始根据比较规则寻找插入位置。如果发现一个已存在的元素与新元素“等价”即!comp(a, b) !comp(b, a)通常意味着a b那么set会拒绝这次插入insert方法返回一个指示失败的迭代器对而multiset则会允许重复将其插入到等价元素序列的合适位置。注意这里的“等价”与“相等”在概念上略有不同。它取决于你提供的比较函数对象。如果你自定义了一个只比较部分成员变量的比较器那么即使两个对象并非完全相等也可能被视为“等价”而被set拒绝。这是一个常见的陷阱。2.2 关键迭代器与查找性能由于元素有序set/multiset的迭代器是“双向迭代器”并且进行中序遍历左-根-右时得到的就是升序序列。这带来了几个强大的衍生能力begin()和rbegin()分别获取最小第一个和最大最后一个元素的迭代器。lower_bound(key)和upper_bound(key)这是两个极其重要的方法。lower_bound(key)返回指向第一个不小于key的元素的迭代器upper_bound(key)返回指向第一个大于key的元素的迭代器。对于set它们可以用来确定一个元素是否存在[lower_bound, upper_bound)是一个空区间则表示不存在或者进行范围查询。对于multiset它们可以定位到所有等价key的元素范围。equal_range(key)直接返回一个迭代器对pair分别等同于lower_bound(key)和upper_bound(key)。对于multiset中查找所有相同元素这是最高效的方式。查找操作find(key)的平均复杂度也是O(log n)。虽然对于只存在一次的查找find和lower_bound可能看起来效果一样但lower_bound在后续需要范围操作时更有优势。3. 基础操作与内存模型剖析3.1 容器的构造与初始化创建set/multiset有多种方式理解每种方式背后的开销和适用场景很重要。#include iostream #include set #include vector int main() { // 1. 默认构造空集合使用默认比较器std::lessT std::setint s1; // 2. 使用迭代器范围构造常用于从其他容器初始化 std::vectorint vec {7, 3, 5, 3, 1, 9}; std::setint s2(vec.begin(), vec.end()); // s2: {1, 3, 5, 7, 9} std::multisetint ms1(vec.begin(), vec.end()); // ms1: {1, 3, 3, 5, 7, 9} // 3. 使用初始化列表C11最直观的初始化方式 std::setint s3 {4, 2, 4, 6, 1}; // s3: {1, 2, 4, 6} // 4. 自定义比较器例如希望set按降序存储 struct DescendingCompare { bool operator()(int a, int b) const { return a b; // 降序规则 } }; std::setint, DescendingCompare s4 {1, 3, 2}; // 迭代顺序3, 2, 1 // 5. 拷贝构造与移动构造C11 std::setint s5(s2); // 拷贝构造深拷贝所有节点 std::setint s6(std::move(s2)); // 移动构造s2变为有效但未指定状态通常为空 // 此时s2已不再拥有原来的数据操作s2需先赋值 return 0; }移动构造在传递容器所有权时非常高效它通常只复制几个内部指针如根节点指针常数时间内完成而拷贝构造则需要复制整棵树复杂度为O(n log n)因为每个元素都需要插入到新树中。3.2 元素的插入与删除艺术插入和删除是修改容器内容的核心其返回值和行为需要仔细理解。插入操作insert(const value_type val): 尝试插入单个元素。对于set返回一个pairiterator, bool其中iterator指向已存在或新插入的元素bool表示插入是否成功true表示新元素被插入false表示元素已存在。对于multiset总是返回指向新插入元素的迭代器因为总是成功。insert(iterator hint, const value_type val): 提供位置提示hint如果提示准确新元素紧接在hint之后插入则可能将插入复杂度从O(log n)降低到分摊常数时间。但提示错误反而可能略增开销。通常在对插入序列有先验知识时使用。insert(InputIt first, InputIt last): 插入一个范围内的元素。无返回值。删除操作erase(iterator pos): 删除迭代器pos所指元素。迭代器pos必须有效且可解引用。此操作返回被删除元素之后元素的迭代器C11起。复杂度为O(log n)但实际是找到节点O(log n) 删除调整O(1)分摊时间。erase(const key_type key): 删除所有键等于key的元素对于set是0或1个对于multiset可能是多个。返回被删除的元素个数。这是根据值删除最常用的接口。erase(iterator first, iterator last): 删除迭代器范围[first, last)内的所有元素。注意last可以不指向容器末尾。返回last。std::setint s {10, 20, 30, 40, 50}; // 插入示例 auto [it1, success1] s.insert(25); // 插入新元素success1true, it1指向25 auto [it2, success2] s.insert(20); // 元素已存在success2false, it2指向已有的20 // 带提示的插入假设我们知道25应该插在20之后 auto hint s.find(20); if (hint ! s.end()) { s.insert(hint, 25); // 可能更高效 } // 删除示例 size_t num_removed s.erase(30); // num_removed 1 auto it s.find(40); if (it ! s.end()) { it s.erase(it); // 删除40it现在指向50或end() } s.erase(s.begin(), std::next(s.begin(), 2)); // 删除前两个元素实操心得对于multiset的删除erase(key)会删除所有匹配项这有时不是你想要的行为。如果你只想删除一个应该先用find或lower_bound获取一个迭代器然后用erase(iterator)删除它。另外在遍历容器并删除元素时经典的for循环很容易因为迭代器失效而出错。正确做法是使用erase返回的迭代器或者使用C20的std::erase_if或者在循环前收集需要删除的键。3.3 查找、计数与范围查询实战查找是set/multiset的看家本领。除了基本的find更要熟练掌握基于有序性的范围查询。std::multisetint ms {10, 20, 20, 20, 30, 40, 50}; // 1. find: 找到任意一个等于20的元素 auto it_find ms.find(20); if (it_find ! ms.end()) { std::cout Found: *it_find std::endl; } // 2. count: 计算等于20的元素个数 size_t c ms.count(20); // c 3 // 3. lower_bound / upper_bound: 定位范围 auto low ms.lower_bound(20); // 指向第一个20 auto up ms.upper_bound(20); // 指向第一个大于20的元素即30 // 区间 [low, up) 包含了所有的20 // 4. equal_range: 一次性获取范围最推荐用于multiset auto range ms.equal_range(20); for (auto it range.first; it ! range.second; it) { std::cout *it ; // 输出: 20 20 20 } std::cout std::endl; // 5. 利用有序性进行范围遍历输出所有 [25, 45] 之间的元素 auto start ms.lower_bound(25); // 第一个 25 的元素 (30) auto end ms.upper_bound(45); // 第一个 45 的元素 (50) for (auto it start; it ! end; it) { std::cout *it ; // 输出: 30 40 }对于setcount的返回值只能是0或1因此常被用作contains检查C20之前。equal_range对于set同样有用其返回的区间要么为空元素不存在要么只包含一个元素。4. 高级用法、性能考量与避坑指南4.1 自定义比较函数与透明比较器默认情况下setint的比较器是std::lessint它要求查找时传入的键类型也是int。但有时我们存储的是复杂对象却想用其部分成员或转换后的值进行查找。传统做法可能导致不必要的临时对象构造。自定义比较函数对象struct Person { std::string name; int id; }; // 方式1定义函数对象 struct CompareById { bool operator()(const Person a, const Person b) const { return a.id b.id; } }; std::setPerson, CompareById personSet; personSet.insert({Alice, 100}); personSet.insert({Bob, 101}); // 查找时必须构造一个完整的Person对象作为键 auto it personSet.find(Person{, 100}); // 需要临时构造PersonC14引入了“透明比较器”允许比较器接受不同类型的参数从而避免临时对象的构造。你需要使用std::less钻石操作符或自定义一个具有is_transparent标记的比较器。// 方式2使用透明比较器 (C14) struct CompareByIdTransparent { using is_transparent void; // 关键声明透明性 bool operator()(const Person a, const Person b) const { return a.id b.id; } bool operator()(int id, const Person b) const { return id b.id; } bool operator()(const Person a, int id) const { return a.id id; } }; std::setPerson, CompareByIdTransparent personSet2; personSet2.insert({Alice, 100}); // 现在可以直接用int查找无需构造Person auto it2 personSet2.find(100); // 高效直接调用 operator()(int, const Person)标准库提供的std::less就是一个透明的函数对象当你使用std::setint, std::less时就可以用任何能与int比较的类型来查找。4.2 迭代器失效与线程安全迭代器失效规则是使用STL容器必须牢记的。对于set/multiset插入操作不会使任何迭代器失效除了被删除元素的迭代器当然会失效。因为红黑树的插入是节点级的不影响其他节点的链接关系。删除操作只会使指向被删除元素的迭代器失效。其他迭代器包括指向其他元素的迭代器仍然有效。这意味着你可以安全地在遍历过程中插入元素但要注意不要破坏排序顺序的逻辑但在遍历过程中删除元素时必须使用erase返回的迭代器来更新循环变量或者采用“先收集后删除”的策略。线程安全方面STL容器本身不是线程安全的。如果多个线程同时读写同一个set必须使用互斥锁如std::mutex进行同步。一个常见的模式是“读多写少”场景可以使用读写锁如std::shared_mutexC17来提升并发读的性能。4.3 性能瓶颈分析与优化策略虽然O(log n)的复杂度已经很优秀但在极端性能敏感的场景下仍有优化空间。批量插入优化如果预先知道所有要插入的元素一次性通过迭代器范围构造set通常比多次调用insert更快。因为构造函数内部可能采用更优化的批量构建算法尽管标准未规定但实现通常会优化。使用unordered_set替代如果你不需要元素有序只关心存在性检查和快速插入删除那么std::unordered_set基于哈希表的平均时间复杂度是O(1)通常更快。但代价是元素无序迭代顺序不确定且最坏情况复杂度可能退化。谨慎使用count对于multisetcount(key)可能需要遍历所有等价元素复杂度是O(log n k)其中k是等价元素的数量。如果你只需要知道是否存在用find检查是否等于end()即可。如果你需要所有等价元素用equal_range获取迭代器范围再计算距离std::distance可能更高效因为distance对于双向迭代器是线性的但count内部可能也是类似实现需视实现而定但equal_range给了你操作元素的灵活性。内存局部性红黑树是节点式存储内存不连续对CPU缓存不友好。如果数据量巨大且访问模式是顺序扫描排序后的vector结合二分查找std::lower_bound可能性能更好因为vector内存连续缓存命中率高。这是一种典型的“时空权衡”。4.4 典型应用场景与代码示例场景一维护一个动态的有序唯一集合如在线用户列表class OnlineUserManager { private: std::setstd::string onlineUsers; // 按用户名排序的在线用户集合 std::mutex mtx; // 保证线程安全 public: void userLoggedIn(const std::string username) { std::lock_guardstd::mutex lock(mtx); auto [it, success] onlineUsers.insert(username); if (success) { std::cout username is now online.\n; } else { std::cout username is already online.\n; } } void userLoggedOut(const std::string username) { std::lock_guardstd::mutex lock(mtx); if (onlineUsers.erase(username) 0) { std::cout username logged out.\n; } } bool isUserOnline(const std::string username) const { std::lock_guardstd::mutex lock(mtx); return onlineUsers.find(username) ! onlineUsers.end(); } void printAllUsers() const { std::lock_guardstd::mutex lock(mtx); for (const auto user : onlineUsers) { std::cout user ; } std::cout std::endl; } };场景二使用multiset实现一个简单的排行榜允许并列class ScoreRanking { private: std::multisetint, std::greaterint scores; // 降序排列允许重复分数 public: void addScore(int score) { scores.insert(score); } // 获取第rank名的分数rank从1开始 int getScoreByRank(size_t rank) const { if (rank 0 || rank scores.size()) return -1; auto it scores.begin(); std::advance(it, rank - 1); return *it; } // 获取分数为score的排名范围由于允许并列一个分数可能对应多个名次 std::pairsize_t, size_t getRankRange(int score) const { // 注意在降序multiset中lower_bound/upper_bound的含义因比较器而反 // 我们需要找到第一个 score 和第一个 score 的位置 // 更清晰的做法使用equal_range auto range scores.equal_range(score); // 因为比较器是greater所以equal_range基于greater比较 size_t first_rank std::distance(scores.begin(), range.first) 1; size_t last_rank std::distance(scores.begin(), range.second); return {first_rank, last_rank}; } void printTopN(size_t n) const { auto it scores.begin(); for (size_t i 0; i n it ! scores.end(); i, it) { std::cout Rank i1 : *it std::endl; } } };场景三利用set实现集合运算交集、并集、差集templatetypename T std::setT set_union(const std::setT a, const std::setT b) { std::setT result; std::set_union(a.begin(), a.end(), b.begin(), b.end(), std::inserter(result, result.begin())); return result; } templatetypename T std::setT set_intersection(const std::setT a, const std::setT b) { std::setT result; std::set_intersection(a.begin(), a.end(), b.begin(), b.end(), std::inserter(result, result.begin())); return result; } templatetypename T std::setT set_difference(const std::setT a, const std::setT b) { std::setT result; std::set_difference(a.begin(), a.end(), b.begin(), b.end(), std::inserter(result, result.begin())); return result; }5. 常见问题、调试技巧与最佳实践5.1 自定义类型作为键的陷阱当你将自定义类型放入set时必须确保该类型满足“严格弱序”要求或者提供自定义的比较器。常见的坑是比较器不一致如果两个元素a和bcomp(a, b)和comp(b, a)同时为false则它们被视为等价。如果你的比较逻辑写错了可能导致意外的“去重”或元素丢失。比较器状态变化比较器对象在容器生命周期内应保持状态不变。如果比较器内部有状态且发生变化容器的排序规则就被破坏了后续行为是未定义的。修改键值set中的元素键是const的你不能直接修改它因为这会破坏红黑树的排序不变性。如果需要修改安全的做法是先删除元素修改后再插入。对于包含可变成员的结构可以考虑将可变部分放在mutable成员中但这需要非常小心。struct BadKey { int id; std::string name; // 错误没有定义比较运算符无法放入std::setBadKey }; struct GoodKey { int id; std::string name; // 方法1重载 operator bool operator(const GoodKey other) const { return id other.id; // 按id排序 // 如果id相等可以继续比较name: return std::tie(id, name) std::tie(other.id, other.name); } }; std::setGoodKey okSet; // 可以工作5.2 迭代与删除的经典错误std::setint s {1, 2, 3, 4, 5}; // 错误示例1在基于范围的for循环中删除当前元素导致迭代器失效 for (auto it s.begin(); it ! s.end(); it) { if (*it % 2 0) { s.erase(it); // 错误erase后it失效后续it行为未定义 } } // 正确做法1利用erase返回值更新迭代器 (C11起) for (auto it s.begin(); it ! s.end(); ) { if (*it % 2 0) { it s.erase(it); // erase返回下一个有效迭代器 } else { it; } } // 正确做法2先收集要删除的键遍历结束后再删除适用于复杂判断逻辑 std::vectorint keysToRemove; for (const auto val : s) { if (val % 2 0) { keysToRemove.push_back(val); } } for (int key : keysToRemove) { s.erase(key); } // 正确做法3使用C20的 std::erase_if (最简洁) std::erase_if(s, [](int val) { return val % 2 0; });5.3 性能问题排查清单当你怀疑set/multiset性能不佳时可以按以下步骤排查比较器开销自定义比较器是否过于复杂每次比较都涉及昂贵的计算如字符串比较、深拷贝吗考虑使用指针或引用或预计算比较键。内存碎片频繁的插入删除可能导致内存碎片。如果容器大小相对稳定可以考虑使用std::vector排序后二分查找。算法选择你真的需要有序性吗如果不需要unordered_set可能是更好的选择。你频繁调用countonmultiset吗考虑用equal_range。数据量数据量很小比如少于100个元素时set的O(log n)优势可能被其常数因子动态内存分配、指针跳转抵消简单的线性结构如vector遍历可能更快。进行性能测试Profiling是唯一确定的方式。5.4 最佳实践总结选择合适的容器需要有序、唯一 -set需要有序、允许重复 -multiset只需要快速查找、不关心顺序 -unordered_set数据基本静态、需要频繁遍历 - 排序的vector。善用透明比较器在C14及以上使用std::less或自定义透明比较器来避免查找时的临时对象构造。批量操作优先在可能的情况下使用范围构造和范围插入而不是循环插入单个元素。理解迭代器失效规则在遍历中删除元素时务必使用erase返回的迭代器或先收集键再删除。自定义键类型要谨慎确保比较逻辑满足严格弱序并且比较器无状态或状态稳定。考虑线程安全在多线程环境中访问容器必须进行适当的同步。性能测试是关键任何关于性能的假设都应通过实际的基准测试来验证特别是在不同数据规模和操作比例下。

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

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

相关文章

SpringBoot-17-MyBatis动态SQL标签之常用标签

文章目录 1 代码1.1 实体User.java1.2 接口UserMapper.java1.3 映射UserMapper.xml1.3.1 标签if1.3.2 标签if和where1.3.3 标签choose和when和otherwise1.4 UserController.java2 常用动态SQL标签2.1 标签set2.1.1 UserMapper.java2.1.2 UserMapper.xml2.1.3 UserController.ja…

wordpress后台更新后 前端没变化的解决方法

使用siteground主机的wordpress网站,会出现更新了网站内容和修改了php模板文件、js文件、css文件、图片文件后,网站没有变化的情况。 不熟悉siteground主机的新手,遇到这个问题,就很抓狂,明明是哪都没操作错误&#x…

网络编程(Modbus进阶)

思维导图 Modbus RTU(先学一点理论) 概念 Modbus RTU 是工业自动化领域 最广泛应用的串行通信协议,由 Modicon 公司(现施耐德电气)于 1979 年推出。它以 高效率、强健性、易实现的特点成为工业控制系统的通信标准。 包…

UE5 学习系列(二)用户操作界面及介绍

这篇博客是 UE5 学习系列博客的第二篇,在第一篇的基础上展开这篇内容。博客参考的 B 站视频资料和第一篇的链接如下: 【Note】:如果你已经完成安装等操作,可以只执行第一篇博客中 2. 新建一个空白游戏项目 章节操作,重…

IDEA运行Tomcat出现乱码问题解决汇总

最近正值期末周,有很多同学在写期末Java web作业时,运行tomcat出现乱码问题,经过多次解决与研究,我做了如下整理: 原因: IDEA本身编码与tomcat的编码与Windows编码不同导致,Windows 系统控制台…

利用最小二乘法找圆心和半径

#include <iostream> #include <vector> #include <cmath> #include <Eigen/Dense> // 需安装Eigen库用于矩阵运算 // 定义点结构 struct Point { double x, y; Point(double x_, double y_) : x(x_), y(y_) {} }; // 最小二乘法求圆心和半径 …

使用docker在3台服务器上搭建基于redis 6.x的一主两从三台均是哨兵模式

一、环境及版本说明 如果服务器已经安装了docker,则忽略此步骤,如果没有安装,则可以按照一下方式安装: 1. 在线安装(有互联网环境): 请看我这篇文章 传送阵>> 点我查看 2. 离线安装(内网环境):请看我这篇文章 传送阵>> 点我查看 说明&#xff1a;假设每台服务器已…

XML Group端口详解

在XML数据映射过程中&#xff0c;经常需要对数据进行分组聚合操作。例如&#xff0c;当处理包含多个物料明细的XML文件时&#xff0c;可能需要将相同物料号的明细归为一组&#xff0c;或对相同物料号的数量进行求和计算。传统实现方式通常需要编写脚本代码&#xff0c;增加了开…

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器的上位机配置操作说明

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器专为工业环境精心打造&#xff0c;完美适配AGV和无人叉车。同时&#xff0c;集成以太网与语音合成技术&#xff0c;为各类高级系统&#xff08;如MES、调度系统、库位管理、立库等&#xff09;提供高效便捷的语音交互体验。 L…

(LeetCode 每日一题) 3442. 奇偶频次间的最大差值 I (哈希、字符串)

题目&#xff1a;3442. 奇偶频次间的最大差值 I 思路 &#xff1a;哈希&#xff0c;时间复杂度0(n)。 用哈希表来记录每个字符串中字符的分布情况&#xff0c;哈希表这里用数组即可实现。 C版本&#xff1a; class Solution { public:int maxDifference(string s) {int a[26]…

【大模型RAG】拍照搜题技术架构速览:三层管道、两级检索、兜底大模型

摘要 拍照搜题系统采用“三层管道&#xff08;多模态 OCR → 语义检索 → 答案渲染&#xff09;、两级检索&#xff08;倒排 BM25 向量 HNSW&#xff09;并以大语言模型兜底”的整体框架&#xff1a; 多模态 OCR 层 将题目图片经过超分、去噪、倾斜校正后&#xff0c;分别用…

【Axure高保真原型】引导弹窗

今天和大家中分享引导弹窗的原型模板&#xff0c;载入页面后&#xff0c;会显示引导弹窗&#xff0c;适用于引导用户使用页面&#xff0c;点击完成后&#xff0c;会显示下一个引导弹窗&#xff0c;直至最后一个引导弹窗完成后进入首页。具体效果可以点击下方视频观看或打开下方…

接口测试中缓存处理策略

在接口测试中&#xff0c;缓存处理策略是一个关键环节&#xff0c;直接影响测试结果的准确性和可靠性。合理的缓存处理策略能够确保测试环境的一致性&#xff0c;避免因缓存数据导致的测试偏差。以下是接口测试中常见的缓存处理策略及其详细说明&#xff1a; 一、缓存处理的核…

龙虎榜——20250610

上证指数放量收阴线&#xff0c;个股多数下跌&#xff0c;盘中受消息影响大幅波动。 深证指数放量收阴线形成顶分型&#xff0c;指数短线有调整的需求&#xff0c;大概需要一两天。 2025年6月10日龙虎榜行业方向分析 1. 金融科技 代表标的&#xff1a;御银股份、雄帝科技 驱动…

观成科技:隐蔽隧道工具Ligolo-ng加密流量分析

1.工具介绍 Ligolo-ng是一款由go编写的高效隧道工具&#xff0c;该工具基于TUN接口实现其功能&#xff0c;利用反向TCP/TLS连接建立一条隐蔽的通信信道&#xff0c;支持使用Let’s Encrypt自动生成证书。Ligolo-ng的通信隐蔽性体现在其支持多种连接方式&#xff0c;适应复杂网…

铭豹扩展坞 USB转网口 突然无法识别解决方法

当 USB 转网口扩展坞在一台笔记本上无法识别,但在其他电脑上正常工作时,问题通常出在笔记本自身或其与扩展坞的兼容性上。以下是系统化的定位思路和排查步骤,帮助你快速找到故障原因: 背景: 一个M-pard(铭豹)扩展坞的网卡突然无法识别了,扩展出来的三个USB接口正常。…

未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?

编辑&#xff1a;陈萍萍的公主一点人工一点智能 未来机器人的大脑&#xff1a;如何用神经网络模拟器实现更智能的决策&#xff1f;RWM通过双自回归机制有效解决了复合误差、部分可观测性和随机动力学等关键挑战&#xff0c;在不依赖领域特定归纳偏见的条件下实现了卓越的预测准…

Linux应用开发之网络套接字编程(实例篇)

服务端与客户端单连接 服务端代码 #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> #include <pthread.h> …

华为云AI开发平台ModelArts

华为云ModelArts&#xff1a;重塑AI开发流程的“智能引擎”与“创新加速器”&#xff01; 在人工智能浪潮席卷全球的2025年&#xff0c;企业拥抱AI的意愿空前高涨&#xff0c;但技术门槛高、流程复杂、资源投入巨大的现实&#xff0c;却让许多创新构想止步于实验室。数据科学家…

深度学习在微纳光子学中的应用

深度学习在微纳光子学中的主要应用方向 深度学习与微纳光子学的结合主要集中在以下几个方向&#xff1a; 逆向设计 通过神经网络快速预测微纳结构的光学响应&#xff0c;替代传统耗时的数值模拟方法。例如设计超表面、光子晶体等结构。 特征提取与优化 从复杂的光学数据中自…