应使用 std::unordered_map 统计频次后导出为 vector 再按 value 降序排序,频次相同时按 key 升序确保稳定:因 map 不支持原生按值排序,unordered_map 插入更高效。

用 std::map 统计字符频次再按值排序
直接遍历字符串,用 std::map<char int></char> 或 std::unordered_map<char int></char> 计数是最直观的做法。但注意:std::map 默认按键(字符)升序,不是按频次;必须导出到容器再排序。
常见错误是试图对 map 原地按 value 排序——C++ 标准容器不支持按 value 排序的原生操作。
- 用
std::unordered_map计数更高效(平均 O(1) 插入),尤其字符串很长时 - 导出为
std::vector<:pair int>></:pair>,再用std::sort+ lambda 按 second(频次)降序 - 若频次相同,可加二级排序:比如按字符 ASCII 升序,避免结果不稳定
std::unordered_map<char, int> freq;
for (char c : s) freq[c]++;
std::vector<std::pair<char, int>> vec(freq.begin(), freq.end());
std::sort(vec.begin(), vec.end(), [](const auto& a, const auto& b) {
return a.second > b.second || (a.second == b.second && a.first < b.first);
});
处理大小写和非字母字符要提前明确规则
“字符频率排序”没说是否区分大小写、是否忽略空格或标点。实际中这直接影响结果,且容易被忽略。
- 默认 C++ 是区分大小写的:
'A'和'a'算两个不同字符 - 若需忽略大小写,统一转小写(
std::tolower)再统计,注意传unsigned char防止负值 UB - 若只关心字母,循环中加
if (std::isalpha(c))过滤;否则数字、空格、符号全计入
性能敏感场景慎用 std::sort 对长频次表排序
如果字符串极长(比如 MB 级),而不同字符种类其实很少(ASCII 只有 256 种可能),排序 vector 的开销远小于计数本身,没问题。但若字符集极大(如 UTF-8 字符串未做编码归一化),或需实时流式统计,就得换思路。
立即学习“C++免费学习笔记(深入)”;
- 对固定小字符集(如纯 ASCII),可用
std::array<int></int>替代 map,O(1) 访问,省去哈希/红黑树开销 - 若只要前 N 高频字符,用
std::priority_queue(最大堆)比全排序更高效,O(k log n),k 是取 Top-K 数量 -
std::priority_queue默认最大堆,自定义比较时注意:要按频次大优先,所以用std::less或默认即可
输出时注意字符不可见导致的调试困惑
排序后打印,遇到空格、制表符、换行符等控制字符,控制台可能显示为空白或异常,误以为没统计到。
- 调试时建议对
char做显式格式化:比如printf("'%c': %d\n", c, cnt)或用static_cast<int>(c)</int>打印 ASCII 码 - 若字符串含中文等宽字符(UTF-8 编码),
char级统计会拆成多个字节,结果完全失真——此时必须先按 UTF-8 解码为 Unicode code point,再统计,不能直接遍历char
真正要处理多字节字符,得用 std::string_view + UTF-8 解码库(如 utf8cpp),或者改用 std::u8string(C++20)配合 std::mbrtoc8,但这已超出“字符频率排序”的原始假设边界。


















