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

C++ 第四阶段 STL 容器 - 第五讲:详解 std::set 与 std::unordered_set

目录

📌 一、std::set 与 std::unordered_set 概述

📌 二、std::set 详解

1. 核心特性

2. 常用函数解析

3. 自定义比较函数

📌 三、std::unordered_set 详解

1. 核心特性

2. 常用函数解析

3. 自定义哈希与比较函数

📌 四、性能对比与优化建议

1. 性能对比表

2. 优化建议

📌 五、常见陷阱与解决方案

1. 修改 std::set 中的元素

2. std::unordered_set 的 rehash

3. 自定义类型的哈希冲突

📌 六、典型应用场景

1. 去重场景

2. 快速查找

3. 有序集合操作

🧠 总结

C++从入门到入土学习导航_c++学习进程-CSDN博客


📌 一、std::set 与 std::unordered_set 概述

std::setstd::unordered_set 是 C++ STL 中的 集合容器,用于存储唯一的元素集合。它们的核心区别在于:

特性std::setstd::unordered_set
底层实现红黑树(自动排序)哈希表(无序)
元素顺序升序排列无序
查找复杂度O(log n)平均 O(1)(最坏 O(n))
内存占用较高(红黑树节点指针)较低(哈希桶)
适用场景需要有序集合快速查找/插入/删除

📌 二、std::set 详解

1. 核心特性

  • 唯一性:所有元素唯一。
  • 自动排序:元素按升序排列(默认使用 < 运算符)。
  • 双向迭代器:支持顺序遍历。
  • 动态内存管理:自动调整红黑树结构。

2. 常用函数解析

#include <set>
#include <iostream>int main() {// 1. 初始化std::set<int> s1;std::set<int> s2 = {1, 3, 2};std::set<int> s3(s2.begin(), s2.end());// 2. 插入与删除s1.insert(5);  // 插入单个元素s1.insert({6, 7, 8});  // 插入多个元素s1.erase(5);  // 删除指定值s1.erase(s1.begin());  // 删除指定迭代器位置// 3. 查找与访问auto it = s1.find(3);if (it != s1.end()) {std::cout << "Found: " << *it << "\n";}// 4. 遍历for (const auto& val : s1) {std::cout << val << " ";  // 输出有序元素}// 5. 特殊操作(仅 set 支持)auto lb = s1.lower_bound(4);  // >=4 的第一个元素auto ub = s1.upper_bound(4);  // >4 的第一个元素return 0;
}

3. 自定义比较函数

struct CompareDesc {bool operator()(int a, int b) const {return a > b;  // 降序排列}
};std::set<int, CompareDesc> s = {3, 1, 2};
for (const auto& val : s) {std::cout << val << " ";  // 输出 3 2 1
}

📌 三、std::unordered_set 详解

1. 核心特性

  • 唯一性:所有元素唯一。
  • 无序性:元素存储顺序由哈希值决定。
  • 快速查找:平均时间复杂度为 O(1)。
  • 哈希冲突:通过链表或开放寻址法解决。

2. 常用函数解析

#include <unordered_set>
#include <iostream>int main() {// 1. 初始化std::unordered_set<int> us1;std::unordered_set<int> us2 = {1, 3, 2};std::unordered_set<int> us3(us2.begin(), us2.end());// 2. 插入与删除us1.insert(5);us1.insert({6, 7, 8});us1.erase(5);us1.erase(us1.begin());  // 删除指定迭代器位置// 3. 查找与访问if (us1.find(3) != us1.end()) {std::cout << "Found\n";}// 4. 遍历for (const auto& val : us1) {std::cout << val << " ";  // 输出无序元素}// 5. 性能优化us1.reserve(100);  // 预分配桶空间us1.max_load_factor(0.75);  // 设置最大负载因子return 0;
}

3. 自定义哈希与比较函数

struct Key {int a, b;bool operator==(const Key& other) const {return a == other.a && b == other.b;}
};struct KeyHash {std::size_t operator()(const Key& k) const {return std::hash<int>()(k.a) ^ (std::hash<int>()(k.b) << 1);}
};std::unordered_set<Key, KeyHash> mySet;
mySet.insert({1, 2});

📌 四、性能对比与优化建议

1. 性能对比表

操作std::setstd::unordered_set
查找O(log n)O(1)(平均)
插入O(log n)O(1)(平均)
删除O(log n)O(1)(平均)
遍历O(n)O(n)
内存占用

2. 优化建议

  • std::set
    • 适用于需要有序集合的场景。
    • 避免频繁插入/删除导致的树结构调整。
    • 使用 lower_bound/upper_bound 提高性能。
  • std::unordered_set
    • 适用于快速查找/插入/删除的场景。
    • 预分配桶空间(reserve())减少 rehash 次数。
    • 调整负载因子(max_load_factor())优化内存使用。
    • 自定义高效的哈希函数和比较器。

📌 五、常见陷阱与解决方案

1. 修改 std::set 中的元素

  • 问题std::set 中的元素不可直接修改,否则会破坏排序。
  • 解决方案:删除旧元素并插入新元素。
std::set<int> s = {1, 2, 3};
s.erase(2);
s.insert(4);

2. std::unordered_set 的 rehash

  • 问题:插入元素可能导致 rehash,迭代器失效。
  • 解决方案:避免在遍历时直接修改容器,或使用临时容器。
for (auto it = us.begin(); it != us.end(); ) {if (*it % 2 == 0) {it = us.erase(it);  // 安全删除} else {++it;}
}

3. 自定义类型的哈希冲突

  • 问题:自定义类型未定义哈希函数或比较器。
  • 解决方案:重载 operator== 和自定义哈希函数。
struct Key {int a, b;bool operator==(const Key& other) const {return a == other.a && b == other.b;}
};struct KeyHash {std::size_t operator()(const Key& k) const {return std::hash<int>()(k.a) ^ (std::hash<int>()(k.b) << 1);}
};std::unordered_set<Key, KeyHash> mySet;

📌 六、典型应用场景

1. 去重场景

  • 适用场景:统计唯一元素数量。
std::unordered_set<std::string> uniqueWords;
std::string text = "hello world hello";
std::istringstream iss(text);
std::string word;
while (iss >> word) {uniqueWords.insert(word);
}
std::cout << "Unique words: " << uniqueWords.size() << "\n";

2. 快速查找

  • 适用场景:验证元素是否存在。
std::set<int> blacklist = {1001, 1002, 1003};
if (blacklist.find(1001) != blacklist.end()) {std::cout << "Blocked!\n";
}

3. 有序集合操作

  • 适用场景:需要按顺序处理元素。
std::set<int> sortedData = {5, 1, 3};
for (int val : sortedData) {std::cout << val << " ";  // 输出 1 3 5
}

🧠 总结

容器优点缺点适用场景
std::set有序、稳定迭代器插入/删除较慢需要排序、范围查询
std::unordered_set查找/插入快无序、迭代器可能失效快速查找、去重
http://www.lryc.cn/news/578085.html

相关文章:

  • 【甲方安全建设】SDL基线建设及审计评估
  • Linux习题
  • 机器学习,支持向量机svm和决策树xgboost介绍
  • 【读代码】TradingAgents:基于多智能体LLM的金融交易框架深度解析
  • 大模型的开发应用(十六):Agent 与 LangGraph基础
  • Waiting for another flutter command to release the startup lock...解决方法
  • 9.6 视觉专家模块+1536超清解析!智谱CogVLM-9B多模态模型中文场景实战评测,性能炸裂吊打LLaVA
  • Python 机器学习实战:泰坦尼克号生还者预测 (从数据探索到模型构建)
  • Spring Security 鉴权与授权详解(前后端分离项目)
  • java后端http接口流式输出到前端
  • 使用OpenSSL接口读取pem编码格式文件中的证书
  • Redis初识第七期---ZSet的命令和应用场景
  • VRR(可变刷新率)和QMS(快速媒体切换)
  • 集群【运维】麒麟V10挂载本地yum源
  • OpenCV计算机视觉实战(14)——直方图均衡化
  • 【期末分布式】分布式的期末考试资料大题整理
  • UI前端大数据处理挑战与对策:保障数据安全与隐私
  • 【知识】RPC和gRPC
  • Reactor操作符的共享与复用
  • Excel数据匹配合并工具
  • Linux 系统管理:自动化运维与容器化部署
  • 2025年数字信号、计算机通信与软件工程国际会议(DSCCSE 2025)
  • postman接口测试全部流程
  • Git 简介安装教程
  • [附源码+数据库+毕业论文]基于Spring+MyBatis+MySQL+Maven+jsp实现的校园服务平台管理系统,推荐!
  • Fiddler中文版抓包工具如何帮助前端开发者高效调试
  • 我的第一个开源项目:用Python搭建轻量级静态网页服务器—— 零基础也能实现的Web开发初体验
  • 鸿蒙应用开发:ArkTS中接口的声明和使用
  • SQL优化(插入、主键、order by、group by)
  • 关于 java:8. Java 内存模型与 JVM 基础