#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 核心特性(重中之重,必记)

  1. 一一映射,键唯一key(键) 是唯一的、不允许重复的;value(值) 可以重复,无限制。
  2. 自动排序:map 会默认按照 key 的「升序」自动排序(底层是红黑树实现),遍历的时候拿到的永远是有序的键值对。
  3. 高效查找:根据 key 查找对应的 value 效率极高,时间复杂度是 O(log n),比数组遍历快得多。
  4. 键值类型灵活keyvalue 可以是任意C++基础类型(int/string/char等),也可以是自定义结构体/类。
  5. 补充对比:和 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 表示迭代器指向的键值对的 keyit->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);

  • 返回值:01。因为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);

  • 返回值:01 → 删除成功返回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

✅ 八、核心知识点总结(必记,查漏补缺)

  1. map有序、唯一键值对容器,key唯一、value可重复,默认按键升序排序;
  2. 核心语法:map<key_type, value_type> 容器名,头文件必须加 <map>
  3. 增:m[key]=val 简洁覆盖、m.insert/emplace(key,val) 严谨不覆盖;
  4. 改:m[key]=新值 万能修改法,key存在即覆盖;
  5. 查:优先用find() 安全无副作用,count() 只判断存在性,慎用[]查找;
  6. 删:erase(key) 按key删除,clear() 清空全部;
  7. 遍历:优先用范围for循环,简洁高效;迭代器遍历兼容所有版本;
  8. 核心易错点:用[]查找不存在的key,会自动新增无效键值对!

希望这份讲解对你有帮助,map是C++中最常用的容器之一,掌握后能解决绝大多数「键值映射」的业务场景,加油!💪