#include <iostream>
// write your code here......
#include<map>
using namespace std;
int main() {
char str[100] = { 0 };
cin.getline(str, sizeof(str));
// write your code here......
map<char, int>m;
for(int i=0; str[i]!='\0'; i++){
//更优写法
m[str[i]]++;
// if(m.count(str[i])){
// m[str[i]] += 1;
// }else{
// m[str[i]] = 1;
// }
}
// for(auto &p : m){
// cout<<p.first<<":"<<p.second<<endl;
// }
//最佳写法-无拷贝开销且只读
for(const auto &p : m){
if(p.first != ' '&&!(p.first>=0&&p.first<='9'))cout<<p.first<<":"<<p.second<<endl;
}
return 0;
}
1.char其实是存储ASCII码的整数类型,存储所有 ASCII 字符(字母、数字、符号、空格、控制字符)。
2.所有STL容器的for循环可以使用这种简化写法:const auto &x : m 是常量引用遍历,无拷贝开销且只读。
如果需要修改内容去掉const即可:auto &x : m
cout << "字符出现次数统计:" << endl;
for(map<char, int>::iterator it = m.begin(); it != m.end(); it++) {
cout << "字符:" << it->first << " 次数:" << it->second << endl;
}
3.map的用法
C++ map 容器 超详细完整用法讲解(零基础易懂,全知识点+代码示例)
✅ 一、map 是什么?核心概念(必懂)
map 是 C++ STL标准库 中非常常用的关联式容器,核心本质是:存储「键值对(key - value)」的有序映射集合,可以把它理解成 “字典/词典”:
- 字典里:一个拼音(key) 对应 一个汉字解释(value)
- map里:一个键(key) 对应 一个值(value)
✨ map 核心特性(重中之重,必记)
- 一一映射,键唯一:
key(键) 是唯一的、不允许重复的;value(值) 可以重复,无限制。 - 自动排序:map 会默认按照
key的「升序」自动排序(底层是红黑树实现),遍历的时候拿到的永远是有序的键值对。 - 高效查找:根据
key查找对应的value效率极高,时间复杂度是O(log n),比数组遍历快得多。 - 键值类型灵活:
key和value可以是任意C++基础类型(int/string/char等),也可以是自定义结构体/类。 - 补充对比:和
map相似的还有unordered_map,区别是:unordered_map是无序的(哈希表实现),查找速度更快(O(1));如果业务需要有序的键值对用map,不需要有序则优先用unordered_map。
✅ 二、前置准备:头文件 + 命名空间
使用 map 必须包含专属头文件,无其他依赖,代码开头必写:
#include <iostream> #include <map> // 使用map的核心头文件,缺一不可 #include <string> // 示例中用到string类型,按需添加 using namespace std; // 初学推荐加,省去写std::map的麻烦
✅ 三、map 的创建与初始化(5种常用方式)
语法格式:map<键的类型, 值的类型> 容器名;
注意:尖括号里是先写key类型,再写value类型,两个类型之间用逗号分隔!
方式1:创建「空map」(最常用,推荐)
初始化一个空的映射集合,后续往里面添加数据,日常开发90%的场景用这种
// 示例1:key=int类型,value=string类型 map<int, string> m1; // 示例2:key=string类型,value=int类型 map<string, int> m2; // 示例3:key=char类型,value=double类型 map<char, double> m3;
方式2:创建时直接初始化赋值(C++11及以上,简洁)
适合一开始就知道要存哪些键值对的场景,用大括号+键值对的形式赋值,最直观
// key=int,value=string,初始化3个键值对
map<int, string> m = {
{1, "张三"},
{2, "李四"},
{3, "王五"}
};
方式3:拷贝构造(用一个map创建另一个map)
把已有的map容器,完整复制给新的map,两者内容完全相同
map<int, string> m1 = {{1,"张三"},{2,"李四"}};
map<int, string> m2(m1); // 把m1的内容拷贝给m2
方式4:指定排序规则(默认升序,改为降序)
map默认按键升序排序,如果需要按键降序,初始化时指定排序规则 greater<key类型> 即可
// 正常升序map:key从小到大 1,2,3
map<int, string> m1 = {{1,"a"},{2,"b"},{3,"c"}};
// 降序map:key从大到小 3,2,1
map<int, string, greater<int>> m2 = {{1,"a"},{2,"b"},{3,"c"}};
✅ 四、map 核心操作:增、删、改、查(重中之重,全是高频考点)
这是map的核心用法,所有业务场景都是围绕这4个操作展开,每个操作都配最简代码示例,全部能直接运行!
⭐ 1. 新增元素(2种常用方法,重点)
方法①:使用 [] 运算符 新增/赋值(最简洁,推荐)
语法:map容器名[key] = value;
- 特点:如果这个key不存在 → 直接新增一个「key-value」键值对;
- 如果这个key已经存在 → 会覆盖原有的value值(这个特性后面改值也会用到)。
map<int, string> m; m[1] = "张三"; // key=1不存在,新增 m[2] = "李四"; // key=2不存在,新增 m[1] = "张三丰";// key=1已存在,覆盖原value:张三 → 张三丰
方法②:使用 insert() 插入(严谨写法,不会覆盖)
语法:map容器名.insert( make_pair(key, value) );
- 特点:如果key已存在,插入会失败,不会覆盖原有value,返回插入结果(成功/失败);
- 适合:只需要「新增」,不希望覆盖原有数据的场景,比
[]更严谨。
map<int, string> m; m.insert(make_pair(1, "张三")); // 插入成功 m.insert(make_pair(1, "张三丰"));// key=1已存在,插入失败,value还是"张三"
⭐ 2. 修改元素(1种万能方法,超简单)
map的修改本质就是:对已存在的key,重新赋值value语法:map容器名[key] = 新的value;
原理:因为key是唯一的,只要key存在,用
[]赋值就会直接覆盖原value,这也是map最便捷的修改方式!
map<int, string> m; m[1] = "张三"; // 新增 m[1] = "张三丰";// 修改:key=1的value从张三 → 张三丰 m[2] = "李四"; // 新增 m[2] = "李四四";// 修改:key=2的value从李四 → 李四四
⭐ 3. 查找元素(3种方法,分场景使用,必学+避坑)
map的查找都是根据key查找(不会根据value查找),三种方法各有优劣,重点避坑!
✅ 方法①:使用 find() 查找(最推荐、最安全,必用!)
语法:map容器名.find(key);
- 返回值:迭代器。如果
key存在→ 迭代器指向这个key对应的键值对;如果key不存在→ 迭代器等于map.end() - 核心优点:查找不存在的key,不会产生任何副作用(不会新增元素),这是map查找的标准答案!
map<int, string> m = {{1,"张三"},{2,"李四"}};
// 查找key=1
auto it = m.find(1);
if(it != m.end()){
cout << "找到:key=" << it->first << ", value=" << it->second << endl;
}else{
cout << "未找到key=1" << endl;
}
// 查找不存在的key=3
it = m.find(3);
if(it == m.end()){
cout << "未找到key=3" << endl;
}
小知识点:
it->first表示迭代器指向的键值对的 key,it->second表示对应的 value
✅ 方法②:使用 [] 运算符 查找(简洁但有【大坑】,慎用!)
语法:map容器名[key];
- 优点:写法简单,如果key存在,能直接拿到对应的value;
- 致命缺点:如果查找的key不存在 → map会自动新增一个「key:默认value」的键值对,会凭空增加无效数据,这是初学map的高频踩坑点!!!
map<int, string> m = {{1,"张三"}};
cout << m[1] << endl; // 正确:输出 张三
cout << m[3] << endl; // 大坑!key=3不存在,map会自动新增 {3, ""} 这个键值对
cout << m.size() << endl; // 输出 2,而不是1!!!
✅ 方法③:使用 count() 判断key是否存在(只做存在性判断)
语法:map容器名.count(key);
- 返回值:
0或1。因为map的key是唯一的,所以有这个key返回1,没有返回0 - 适用场景:只需要判断key是否存在,不需要获取对应的value,写法极简!
map<int, string> m = {{1,"张三"},{2,"李四"}};
if(m.count(1)){
cout << "key=1 存在" << endl;
}
if(m.count(3) == 0){
cout << "key=3 不存在" << endl;
}
⭐ 4. 删除元素(3种常用方法,全掌握)
map的删除都是根据key删除,或者清空全部,删除效率很高,语法都很简单,全部实用!
方法①:按key删除(最常用,推荐)
语法:map容器名.erase(key);
- 返回值:
0或1→ 删除成功返回1,key不存在返回0。
map<int, string> m = {{1,"张三"},{2,"李四"},{3,"王五"}};
m.erase(2); // 删除key=2的键值对,成功
m.erase(5); // key=5不存在,无操作,返回0
方法②:清空所有元素
语法:map容器名.clear();
- 作用:删除map中所有的键值对,清空后map变成空容器,元素个数为0。
map<int, string> m = {{1,"张三"},{2,"李四"}};
m.clear(); // 清空全部
cout << m.size() << endl; // 输出 0
方法③:按迭代器删除(了解即可)
语法:map容器名.erase(迭代器);
- 作用:删除迭代器指向的那个键值对,适合遍历的时候删除指定元素。
map<int, string> m = {{1,"张三"},{2,"李四"}};
auto it = m.find(1);
if(it != m.end()){
m.erase(it); // 删除迭代器指向的key=1的键值对
}
✅ 五、map 的遍历(2种常用方式,必学,高频!)
遍历就是依次访问map中的每一个键值对,map是有序的,所以遍历结果永远是按key升序/降序排列的,两种遍历方式足够应对所有场景,第一种优先学,最简洁!
遍历核心知识点:map的每一个元素都是一个
pair<const key, value>类型的「键值对」,first是key,second是value;且key是const的,遍历的时候不能修改key,只能修改value。
方式①:范围for循环遍历(C++11及以上,最简洁,首选!)
语法:for(auto &p : map容器名){ ... }
- 优点:代码量最少、最易懂、书写最快,日常开发99%的场景用这个,推荐!
- 加
&的原因:避免拷贝,提升效率;如果不需要修改value,可以写auto p。
map<int, string> m = {{1,"张三"},{2,"李四"},{3,"王五"}};
// 遍历所有键值对
for(auto &p : m){
cout << "key=" << p.first << ", value=" << p.second << endl;
}
// 遍历的同时修改value
for(auto &p : m){
p.second += "_同学"; // 给每个value追加内容
}
方式②:迭代器遍历(兼容所有C++版本,万能写法)
语法:用map<key,value>::iterator定义迭代器,从begin()到end()遍历
- 优点:兼容所有C++版本(比如老旧编译器),无版本限制;
- 缺点:代码稍微长一点,但是写法固定,记下来即可。
map<int, string> m = {{1,"张三"},{2,"李四"},{3,"王五"}};
// 正向遍历(升序)
for(map<int, string>::iterator it = m.begin(); it != m.end(); it++){
cout << "key=" << it->first << ", value=" << it->second << endl;
}
// 反向遍历(从后往前,降序)
for(map<int, string>::reverse_iterator it = m.rbegin(); it != m.rend(); it++){
cout << "key=" << it->first << ", value=" << it->second << endl;
}
✅ 六、map 常用的其他成员函数(必记,高频)
这些函数都是map的基础工具函数,用法极简,都是一行代码搞定,补充后map的用法就完整了,全部实用!
map<int, string> m = {{1,"张三"},{2,"李四"}};
m.size(); // 返回:map中元素的个数 → 2
m.empty(); // 返回:bool值,空返回true,非空返回false → false
m.max_size(); // 返回:map能存储的最大元素个数(系统限制,了解即可)
m.begin(); // 返回:指向第一个元素的迭代器
m.end(); // 返回:指向最后一个元素的「下一个位置」的迭代器(结束标志)
m.rbegin(); // 返回:反向遍历的第一个迭代器
m.rend(); // 返回:反向遍历的结束标志
m.swap(m2); // 交换两个map的内容(两个map的key/value类型必须相同)
✅ 七、map 完整综合示例代码(可直接复制运行)
把上面所有核心知识点整合到一起,形成一个完整的可运行代码,建议复制到编译器里运行一遍,加深所有知识点的理解:
#include <iostream>
#include <map>
#include <string>
using namespace std;
int main() {
// 1. 创建并初始化map
map<int, string> m = {{1, "张三"}, {2, "李四"}, {3, "王五"}};
// 2. 新增元素
m[4] = "赵六";
m.emplace(5, "钱七");
// 3. 修改元素
m[1] = "张三丰";
// 4. 查找元素
auto it = m.find(2);
if(it != m.end()){
cout << "查找key=2:" << it->second << endl;
}
// 5. 删除元素
m.erase(5);
// 6. 遍历元素
cout << "\n遍历所有元素:" << endl;
for(auto &p : m){
cout << "key=" << p.first << ", value=" << p.second << endl;
}
// 7. 其他常用函数
cout << "\n元素个数:" << m.size() << endl;
cout << "是否为空:" << (m.empty() ? "是" : "否") << endl;
// 清空map
m.clear();
cout << "清空后元素个数:" << m.size() << endl;
return 0;
}
运行结果:
查找key=2:李四 遍历所有元素: key=1, value=张三丰 key=2, value=李四 key=3, value=王五 key=4, value=赵六 元素个数:4 是否为空:否 清空后元素个数:0
✅ 八、核心知识点总结(必记,查漏补缺)
map是有序、唯一键值对容器,key唯一、value可重复,默认按键升序排序;- 核心语法:
map<key_type, value_type> 容器名,头文件必须加<map>; - 增:
m[key]=val简洁覆盖、m.insert/emplace(key,val)严谨不覆盖; - 改:
m[key]=新值万能修改法,key存在即覆盖; - 查:优先用
find()安全无副作用,count()只判断存在性,慎用[]查找; - 删:
erase(key)按key删除,clear()清空全部; - 遍历:优先用范围for循环,简洁高效;迭代器遍历兼容所有版本;
- 核心易错点:用
[]查找不存在的key,会自动新增无效键值对!
希望这份讲解对你有帮助,map是C++中最常用的容器之一,掌握后能解决绝大多数「键值映射」的业务场景,加油!💪

京公网安备 11010502036488号