c++---map和set

这里再提二叉树(二叉搜索树),是为了后面讲解map和set做准备。

一、二叉搜索树

二叉搜索树又称二叉排序树,它或者是一棵空树,或者是具有以下性质的二叉树。

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

二、关联式容器

在STL部分,我们已经接触过STL中的部分容器,比如:vector、list、deque、
forward_list(C++11)等,这些容器统称为序列式容器,因为其底层为线性序列的数据结构,里面
存储的是元素本身。那什么是关联式容器?它与序列式容器有什么区别?

关联式容器也是用来存储数据的,与序列式容器不同的是,其里面存储的是<key, value>结构的
键值对
,在数据检索时比序列式容器效率更高。

补充:什么是键值对

用来表示具有一一对应关系的一种结构,该结构中一般只包含两个成员变量key和valuekey代
表键值,value表示与key对应的信息
。比如:现在要建立一个英汉互译的字典,那该字典中必然
有英文单词与其对应的中文含义,而且,英文单词与其中文含义是一一对应的关系,即通过该应
该单词,在词典中就可以找到与其对应的中文含义。

template <class T1, class T2>  // 定义一个模板结构体,接受两个类型参数 T1 和 T2
struct pair
{typedef T1 first_type;     // 定义 first_type 为第一个模板参数 T1 的别名typedef T2 second_type;    // 定义 second_type 为第二个模板参数 T2 的别名T1 first;                  // 结构体的第一个成员,类型为 T1T2 second;                 // 结构体的第二个成员,类型为 T2// 默认构造函数:使用默认构造函数初始化 first 和 secondpair(): first(T1()), second(T2()){}// 带参数的构造函数:使用给定的参数 a 和 b 分别初始化 first 和 secondpair(const T1& a, const T2& b): first(a), second(b){}
};

三、树形结构的关联式容器

根据应用场景的不桶,STL总共实现了两种不同结构的管理式容器:树型结构哈希结构。树型结
构的关联式容器主要有四种:map、set、multimap、multiset。这四种容器的共同点是:使
用平衡搜索树(即红黑树)作为其底层结果,容器中的元素是一个有序的序列。下面一依次介绍每一个容器。

set容器

set基本介绍:

官方文档:

http://www.cplusplus.com/reference/set/set/?kw=set

1. set是按照一定次序存储元素的容器
2. 在set中,元素的value也标识它(value就是key,类型为T),并且每个value必须是唯一的。
set中的元素不能在容器中修改(元素总是const),但是可以从容器中插入或删除它们。
3. 在内部,set中的元素总是按照其内部比较对象(类型比较)所指示的特定严格弱排序准则进行
排序。
4. set容器通过key访问单个元素的速度通常比unordered_set容器慢,但它们允许根据顺序对
子集进行直接迭代。
5. set在底层是用二叉搜索树(红黑树)实现的。

注意:
1. 与map/multimap不同,map/multimap中存储的是真正的键值对<key, value>,set中只放
value,但在底层实际存放的是由<value, value>构成的键值对。
2. set中插入元素时,只需要插入value即可,不需要构造键值对。
3. set中的元素不可以重复(因此可以使用set进行去重)。
4. 使用set的迭代器遍历set中的元素,可以得到有序序列
5. set中的元素默认按照小于来比较
6. set中查找某个元素,时间复杂度为:O(log n)
7. set中的元素不允许修改(为什么?)

- 维护有序性 :set 有序排列特性依赖底层红黑树结构,直接修改元素值易破坏有序性,如将有序元素 5 改为 2 会扰乱顺序。
- 维护红黑树的平衡性 :红黑树有严格平衡性质以保证操作时间复杂度,修改元素键值易破坏平衡,恢复平衡需复杂操作,增加程序复杂度和运行时间。
- 元素的唯一性 :set 要求元素唯一,插入时会检查重复。直接修改元素值可能导致重复元素出现,违反唯一性要求。
- 迭代器失效问题 :set 迭代器指向树节点,修改元素值可能改变其在树中的位置,导致迭代器指向错误位置,引发未定义行为。
- 解决方法 :不能直接修改元素值,可通过先删除再插入新值,或利用其有序性特点插入新元素后按需删除旧元素来实现类似修改效果。

8. set中的底层使用二叉搜索树(红黑树)来实现。

set的使用:

 声明和初始化

#include <iostream>
#include <set>
using namespace std;int main() 
{// 声明一个 set 容器set<int> s;// 初始化一个 set 容器set<int> s1 = { 1, 3, 5, 7, 9 };return 0;
}

插入元素

#include <iostream>
#include <set>
using namespace std;int main() 
{set<int> s;// 插入单个元素s.insert(10);s.insert(20);s.insert(30);// 输出插入后的 setfor (int num : s){cout << num << " ";}return 0;
}

运行结果:

插入元素

#include <iostream>
#include <set>
#include <vector>
using namespace std;int main() 
{set<int> s;vector<int> vec = { 5, 6, 7, 8, 9 };// 区间插入s.insert(vec.begin(), vec.end());// 输出插入后的 setfor (int num : s) {cout << num << " ";}return 0;
}

综合运用,用pair实现键值对

#include <iostream>
#include <set>
#include <string>
#include <iomanip>
#include <climits>  // 添加INT_MAX所需的头文件// 自定义比较器:按值排序(值大的在前)
struct ValueComparator
{bool operator()(const std::pair<std::string, int>& a,const std::pair<std::string, int>& b) const{// 添加名称比较,确保价格相同时也能正确排序if (a.second != b.second) {return a.second > b.second; // 降序排列}return a.first < b.first;}
};int main()
{// 1. 基本set使用// 自动排序且去重,元素类型为intstd::set<int> numbers = { 5, 2, 8, 2, 1, 4, 3 };std::cout << "1. 基本set使用 (自动排序且唯一):\n";for (int num : numbers){std::cout << num << " ";}std::cout << "\n\n";// 2. 使用set存储pair实现键值对// 默认按pair.first(名称)的字典序排序std::set<std::pair<std::string, int>> products;// 插入产品信息 (名称, 价格)products.insert({ "Laptop", 1200 });products.insert({ "Phone", 800 });products.insert({ "Tablet", 600 });products.insert({ "Headphones", 150 });products.insert({ "Camera", 950 });// 默认按pair.first (名称) 排序std::cout << "2. 按产品名称排序:\n";std::cout << std::left << std::setw(15) << "产品" << "价格\n";std::cout << "-------------------\n";for (const auto& product : products){std::cout << std::setw(15) << product.first << "$" << product.second << "\n";}std::cout << "\n";// 3. 使用自定义比较器按值排序// 使用ValueComparator实现按价格降序排列std::set<std::pair<std::string, int>, ValueComparator> productsByValue;// 插入相同数据for (const auto& product : products){productsByValue.insert(product);}std::cout << "3. 按产品价格排序 (降序):\n";std::cout << std::left << std::setw(15) << "产品" << "价格\n";std::cout << "-------------------\n";for (const auto& product : productsByValue) {std::cout << std::setw(15) << product.first << "$" << product.second << "\n";}std::cout << "\n";// 4. 查找操作 - 修正:只需提供键(名称)// 利用pair的字典序比较,second值用0占位auto it = products.find({ "Tablet", 0 }); // 价格值不重要,只需名称匹配if (it != products.end()){std::cout << "4. 查找结果: 找到产品 - "<< it->first << " ($" << it->second << ")\n\n";}// 5. 更新操作(删除+重新插入)// set无法直接修改键,需删除后重新插入auto old = products.find({ "Phone", 0 }); // 只需名称匹配if (old != products.end()){// 保存实际价格值std::string name = old->first;int price = old->second;products.erase(old);products.insert({ "Phone Pro", 999 });std::cout << "5. 更新操作: " << name << " ($" << price<< ") 已更新为 Phone Pro ($999)\n\n";}// 6. 范围查询修正 - 使用线性扫描代替边界查询// 由于set按名称排序,无法直接使用lower_bound/upper_boundstd::cout << "6. 价格在 $500 到 $1000 之间的产品:\n";std::cout << std::left << std::setw(15) << "产品" << "价格\n";std::cout << "-------------------\n";for (const auto& product : products){if (product.second >= 500 && product.second <= 1000){std::cout << std::setw(15) << product.first << "$" << product.second << "\n";}}std::cout << "\n";// 7. 统计操作修正 - 使用count检查高价产品std::cout << "7. 统计:\n";std::cout << "  产品总数: " << products.size() << "\n";bool hasExpensive = false;for (const auto& product : products){if (product.second > 1000){hasExpensive = true;break;}}std::cout << "  是否有价格超过$1000的产品? "<< (hasExpensive ? "是" : "否") << "\n\n";// 8. 删除操作修正 - 只需提供键(名称)products.erase({ "Headphones", 0 }); // 价格值不重要std::cout << "8. 删除 Headphones 后剩余产品数: " << products.size() << "\n\n";// 9. 综合应用:价格分析int total = 0;int maxPrice = 0;int minPrice = INT_MAX;for (const auto& product : products){int price = product.second;total += price;if (price > maxPrice) maxPrice = price;if (price < minPrice) minPrice = price;}std::cout << "9. 价格分析:\n";std::cout << "  最贵产品: $" << maxPrice << "\n";std::cout << "  最便宜产品: $" << minPrice << "\n";std::cout << "  平均价格: $" << static_cast<double>(total) / products.size() << "\n";return 0;
}

map容器

官方文档

cplusplus.com/reference/map/map/?kw=map

map的基本介绍

std::map  是 C++ STL 中的关联容器,存储键值对,按键有序(默认升序),基于红黑树实现,查找、插入、删除复杂度均为 O(log n)。

核心特性


1. 有序性:元素按键自动排序(可自定义比较规则)。
2. 唯一性:键唯一(重复插入会被忽略)。
3. 键值对结构:类型为  std::pair<const Key, Value
 

基础用法

1. 头文件

#include <map>

2. 创建与初始化

// 空map  
std::map<std::string, int> map1;  // 带初始值  
std::map map2 = {{"apple", 3}, {"banana", 5}};  // 自定义降序排序  
struct CompareDesc { bool operator()(const std::string& a, const std::string& b) const 
{ return a > b; } };  
std::map<std::string, int, CompareDesc> map3;  

3. 插入元素

map["key"] = 10;          // 下标操作(键不存在时创建)  
map.insert({{"key2", 20}});  // insert 函数  
map.emplace("key3", 30);     // C++11 emplace(避免临时对象)  

4.访问元素

//4. 访问元素int val = map["key"];       // 键不存在时创建默认值(可能意外)  
int val_safe = map.at("key"); // 键不存在时抛异常  
auto it = map.find("key");  // 安全查找,返回迭代器  //5. 删除元素map.erase("key");          // 按键删除  
map.erase(it);             // 按迭代器删除  
map.erase(first, last);     // 删除区间 [first, last)  //6. 遍历与查询// 遍历(C++11 范围for)  
for (const auto& [k, v] : map) { std::cout << k << ":" << v; }  // 范围查询  
auto low = map.lower_bound(20);  // 首个 >= 键的元素  
auto high = map.upper_bound(40); // 首个 > 键的元素  

综合应用:

#include <map>
#include <string>
#include <iostream>
#include <cctype>
#include <algorithm> // 包含std::tolowerint main()
{// 使用map统计单词出现次数,键为单词,值为次数std::map<std::string, int> wordCount;std::string text = "This is a test. Test again.";std::string word;// 遍历文本中的每个字符for (char c : text){// 如果是字母,转换为小写并添加到当前单词if (std::isalpha(static_cast<unsigned char>(c)))word += std::tolower(static_cast<unsigned char>(c));// 如果不是字母且当前单词非空,表示一个单词结束else if (!word.empty()){// 将单词加入统计并重置当前单词wordCount[word]++;word.clear();}}// 处理文本末尾的最后一个单词if (!word.empty())wordCount[word]++;// 方法1:使用pair的first和second成员(兼容所有C++版本)std::cout << "Method 1 (using first/second):\n";for (const auto& entry : wordCount){// entry.first为单词,entry.second为次数std::cout << entry.first << ": " << entry.second << "\n";}// 方法2:结构化绑定(需要C++17支持)// 如果编译器支持C++17,可以取消注释以下代码/*std::cout << "\nMethod 2 (using structured binding):\n";for (const auto& [w, c] : wordCount){std::cout << w << ": " << c << "\n";}*/return 0;
}

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

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

相关文章

windows下,podman迁移镜像文件位置

docker-desktop有自带的镜像文件位置迁移功能&#xff0c;但podman-desktop还没有&#xff0c;所以只能自己操作wsl导入导出来实现# 1.一定要先停止当前machine podman machine stop# 2. 导出当前 machine&#xff08;会生成 tar 镜像&#xff09; wsl --export podman-machine…

Champ-基于3D的人物图像到动画视频生成框架

本文转载自&#xff1a;https://www.hello123.com/champ ** 一、&#x1f916; Champ 是什么&#xff1f; 阿里 南大 复旦联手打造的虚拟人动作黑科技&#xff01;Champ 可不是普通动画工具&#xff0c;它能把你随手拍的小视频变成专业级 3D 动画 —— 无论跳舞、打拳还是走…

Thingsboard 3.4 源码运行 Mac Mini

拉取源码 git clone https://github.com/thingsboard/thingsboard.gitjdk11 java -version java version "11.0.27" 2025-04-15 LTS Java(TM) SE Runtime Environment 18.9 (build 11.0.278-LTS-232) Java HotSpot(TM) 64-Bit Server VM 18.9 (build 11.0.278-LTS-23…

【AI大模型面试宝典60题】1-5

目录 Q1:仅编码器(BERT 类)、仅解码器(GPT 类)和完整的编码器-解码器架构各有什么优缺点? 1. 编码器架构 (Encoder-only) - 代表:BERT系列 2. 解码器架构 (Decoder-only) - 代表:GPT系列 3. 编码器-解码器架构 (Encoder-Decoder) - 代表:T5、BART 升华与总结 (总…

macOS中找不到钥匙串访问

如果在macOS中找不到钥匙串访问&#xff0c;请操作如下命令&#xff1a; security list-keychains可以看到类似&#xff1a; “/Library/Keychains/System.keychain” 然后执行&#xff1a; open /Library/Keychains/System.keychain然后可以将应用保留在程序坞中保留。

UCOSIII移植——学习笔记1

本文是笔者在学习 正点原子官方 的《【正点原子】手把手教你学UCOS-III实时操作系统》系列视频时整理的笔记。 视频讲解清晰透彻&#xff0c;非常感谢UP主的无私奉献&#xff01;原课程链接如下&#xff1a; &#x1f449; B站视频链接&#xff1a;【正点原子】手把手教你学UCO…

SpringBootCodeGenerator使用JSqlParser解析DDL CREATE SQL 语句

&#x1f9e0; 使用 JSqlParser 解析 CREATE TABLE SQL 语句详解在数据库开发中&#xff0c;我们常常需要从 SQL 中提取表结构信息&#xff0c;比如字段名、类型、注释等。相比使用正则表达式&#xff0c;JSqlParser 提供了更可靠的方式来解析 SQL 语句&#xff0c;尤其适用于复…

css3新增-网格Grid布局

目录flex弹性布局Gird布局开启网格布局定义网格中的行和列长度值百分比值新单位fr关键字函数minmax(min, max)函数-repeatauto-fill vs auto-fit举例说明grid-template-areasgapgrid-auto-columns和grid-auto-rowsjustify-contentalign-contentjustify-contentalign-contentjus…

最新最强新太极工具3.6 支持Windows和不支持mac电脑,支持免改码,和改码,支持12—18系统

温馨提示&#xff1a;文末有资源获取方式最新最强太极工具3.6支持Windows和Mac计算机&#xff0c;支持无代码更改和代码更改&#xff0c;支持12-18个系统 支持A7-A11芯片、Apple 5s x、iPad A7至A11芯片&#xff0c;支持所有者锁定、激活锁定、无法激活&#xff08;密码界面和禁…

深入浅出 C++20:新特性与实践

C20 是 C 编程语言的一次重要更新&#xff0c;引入了许多新特性和改进&#xff0c;旨在提升代码的简洁性、安全性和性能。本文将详细介绍 C20 的一些核心特性&#xff0c;并通过示例代码帮助读者理解这些特性的应用场景。C20 新特性总结 以下是 C20 的主要新特性及其简要描述&a…

CSS 属性概述

CSS 属性概述 CSS 属性用于控制 HTML 元素的样式和行为&#xff0c;包括布局、颜色、字体、动画等。以下是常用的 CSS 属性分类及示例&#xff1a; 布局相关属性 display: 控制元素的显示方式&#xff0c;如 block、inline、flex、grid。position: 定义元素的定位方式&#…

--- 统一请求入口 Gateway ---

spring cloud gateway 官方文档 Spring Cloud Gateway 中文文档 什么是api网关 对于微服务的每个接口&#xff0c;我们都需要校验请求的权限是否足够&#xff0c;而微服务把项目细化除了许多个接口&#xff0c;若这些接口都要对服务进行权限校验的话&#xff0c;那么无疑加重…

返利app的消息队列架构:基于RabbitMQ的异步通信与解耦实践

返利app的消息队列架构&#xff1a;基于RabbitMQ的异步通信与解耦实践 大家好&#xff0c;我是阿可&#xff0c;微赚淘客系统及省赚客APP创始人&#xff0c;是个冬天不穿秋裤&#xff0c;天冷也要风度的程序猿&#xff01; 在返利app的业务流程中&#xff0c;用户下单、返利计算…

Vue3 响应式失效 debug:Proxy 陷阱导致数据更新异常的深度排查

人们眼中的天才之所以卓越非凡&#xff0c;并非天资超人一等而是付出了持续不断的努力。1万小时的锤炼是任何人从平凡变成超凡的必要条件。———— 马尔科姆格拉德威尔 &#x1f31f; Hello&#xff0c;我是Xxtaoaooo&#xff01; &#x1f308; “代码是逻辑的诗篇&#xff0…

【贪心算法】day10

&#x1f4dd;前言说明&#xff1a; 本专栏主要记录本人的贪心算法学习以及LeetCode刷题记录&#xff0c;按专题划分每题主要记录&#xff1a;&#xff08;1&#xff09;本人解法 本人屎山代码&#xff1b;&#xff08;2&#xff09;优质解法 优质代码&#xff1b;&#xff…

LeetCode算法日记 - Day 42: 岛屿数量、岛屿的最大面积

目录 1. 岛屿数量 1.1 题目解析 1.2 解法 1.3 代码实现 2. 岛屿的最大面积 2.1 题目解析 2.2 解法 2.3 代码实现 1. 岛屿数量 https://leetcode.cn/problems/number-of-islands/ 给你一个由 1&#xff08;陆地&#xff09;和 0&#xff08;水&#xff09;组成的的二维…

短波红外相机在机器视觉检测方向的应用

短波红外相机在机器视觉检测方向的应用短波红外相机&#xff1a;机器视觉的“低成本突破者”一、打破成本困局&#xff1a;短波红外的“平民化”革新二、核心技术&#xff1a;有机材料的“硬核创新”1. 材料革命&#xff1a;有机感光层的优势2. 工艺兼容&#xff1a;嫁接成熟CM…

【数据结构与算法】图 Floyd算法

相关题目&#xff1a; 1334. 阈值距离内邻居最少的城市 - 力扣&#xff08;LeetCode&#xff09; 资料 &#xff1a; Floyd算法原理及公式推导 - 知乎 Floyd 算法是一种经典的动态规划算法&#xff0c;用与求解图中所有顶点之间的最短短路路径。它由Robert Floyd 于1962…

卫星通信天线的指向精度,含义、测量和计算

卫星通信天线的指向精度&#xff0c;含义、测量和计算我们在卫星通信天线的技术规格书中&#xff0c;都会看到天线指向精度这个指标。一般来说&#xff0c;技术规格书上的天线指向精度的参数是这么写的&#xff1a;“天线指向精度≤1/10半功率波束带宽”今天这个文章&#xff0…

基于LSTM与3秒级Tick数据的金融时间序列预测实现

数据加载模块解析 def load_data(filepath):df pd.read_csv(filepath)return df该函数承担基础数据采集职责&#xff0c;通过Pandas库读取CSV格式的高频交易数据&#xff08;典型如股票分笔成交明细&#xff09;。输入参数为文件路径字符串&#xff0c;输出结构化DataFrame对象…