用 std::unordered_map 统计字符频次最高效,平均 O(1) 时间复杂度,适合小范围键(如 256 个 char 值),无需预初始化,但遍历无序;若需 ASCII 序输出须额外排序。

直接用 std::unordered_map 是最常用、最自然的做法,时间复杂度平均 O(1) 插入/查询,适合单字节字符(ASCII 或 UTF-8 编码下按字节计数)。
为什么不用 map 而用 unordered_map
std::map 是红黑树实现,插入和查找都是 O(log n);std::unordered_map 是哈希表,平均 O(1),对字符这种小范围键值(最多 256 个不同 char 值)性能更优,且无序性不影响统计结果。
注意:如果需要按 ASCII 码顺序输出结果,得额外排序,unordered_map 本身不保证遍历顺序。
- 键类型选
char即可,无需unsigned char—— 但若输入含负值(如某些平台char默认 signed),直接作为数组下标会越界,而unordered_map没这问题 - 不要用
map代替unordered_map,语义不清,且int键可能掩盖字符截断问题
统计字符串中每个字符出现次数(基础实例)
核心就是遍历字符串,对每个 char 执行 ++freq[c]。C++11 起支持自动初始化为 0,无需预先检查是否存在。
立即学习“C++免费学习笔记(深入)”;
std::string s = "hello world"; std::unordered_mapfreq; for (char c : s) { ++freq[c]; } // 遍历时注意:c 可能是空格、换行等不可见字符 for (const auto& p : freq) { std::cout << "'" << p.first << "': " << p.second << "\n"; }
处理大小写与空白字符的常见取舍
是否忽略大小写、是否跳过空格/标点,取决于业务需求,不是容器选择问题,而是预处理逻辑:
- 统一转小写:
std::tolower(static_cast(必须 cast,否则(c)) char为负时行为未定义) - 跳过空白:
if (std::isspace(static_cast(c))) continue; - 只统计字母:
if (!std::isalpha(static_cast(c))) continue;
这些判断都应在插入前做,避免把无关字符塞进 map 增加内存和遍历开销。
遇到中文或 UTF-8 多字节字符怎么办
std::unordered_map 按字节计数,对 UTF-8 中文会把一个汉字拆成多个字节分别统计,结果完全错误。
正确做法分两步:
- 用
std::u8string(C++20)或第三方库(如 ICU、utf8cpp)解码 UTF-8 字节流为 Unicode 码点 - 用
std::unordered_map统计码点频率
若仅需粗略统计“UTF-8 编码单元”(即字节),那原方案仍可用,但要明确这不是“字符”频率。
最容易被忽略的是:没意识到 char 在不同平台有符号性差异,以及 UTF-8 下 char 不等于“字符”。这两点一旦出错,调试成本远高于换容器。











