优先选用 std::unordered_map 统计字符频次,因其平均 O(1) 插入优于 std::map 的 O(log n);注意 char 符号性问题,必要时转 unsigned char;排序需导出至 vector 后自定义谓词:先按频次降序,频次相同时按字符 ASCII 升序。

用 std::map 或 std::unordered_map 统计字符频次
直接遍历字符串,对每个字符做计数即可。优先选 std::unordered_map<char int></char>,因为不关心字符顺序,哈希表平均 O(1) 插入比 std::map 的 O(log n) 更快。注意:char 有符号性问题,但 ASCII 字符(0–127)在绝大多数编译器下无影响;若需支持扩展 ASCII 或误传负值,可转为 unsigned char 再投射为 int 做 key。
常见错误是把 std::string::at() 或下标访问结果直接当 int 用,导致符号扩展(如 '\xFF' 变成 -1),但 map key 是 char 类型,本身没问题;真正出错多发生在后续排序时拿 char 当整数比较——所以统计阶段保持类型一致最稳妥。
按频次降序排序,频次相同时按字符 ASCII 升序稳定输出
不能直接对 map 排序(它按键有序),得把键值对搬进 vector,再用 std::sort。排序谓词要写清楚:先比 value(频次)降序,value 相等时比 key(字符)升序。别漏掉“频次相同时的处理”,否则 'a' 和 'b' 都出现 2 次,输出顺序可能每次不同(不稳定)。
std::vector<std::pair<char, int>> freq_vec(mp.begin(), mp.end());
std::sort(freq_vec.begin(), freq_vec.end(), [](const auto& a, const auto& b) {
if (a.second != b.second) return a.second > b.second; // 频次降序
return a.first < b.first; // 字符升序
});
容易踩的坑:std::sort 默认升序,不写谓词会按 pair 的默认比较(先比 first,再比 second),完全不符合需求;另外 lambda 捕获列表用 [] 即可,无需引用或值捕获。
立即学习“C++免费学习笔记(深入)”;
构造结果字符串时避免反复 += 拼接
知道每个字符要重复几次,就用 std::string(count, ch) 构造子串,再用 += 累加。虽然 += 在 C++11 后一般有 amortized O(1) 复杂度,但如果提前 reserve 总长度,能彻底避免多次内存重分配。
总长度就是原字符串 size(),所以一开始就 result.reserve(s.size())。否则对长字符串(比如 10 万字符),频繁扩容可能带来可观开销。
示例片段:
std::string result;
result.reserve(s.size());
for (const auto& p : freq_vec) {
result += std::string(p.second, p.first);
}
边界情况:空串、单字符、全相同字符都必须通过
空字符串输入,map 为空,vector 为空,循环不执行,result 仍是空串——正确。单字符如 "a",map 只有一项,排序后唯一,拼出 "a"。全相同如 "aaa",所有字符频次都是 3,排序只看字符,只有一个 key,也正确。
真正容易被忽略的是非打印字符(如 '\0'、'\t'、'\n'):它们也会被统计和输出。如果业务要求跳过空白或控制字符,得在统计前加过滤条件,例如 if (!std::isprint(c)) continue;。标准题干没提,但实际项目里常要明确这点。


















