用std::map统计字符频次后,需转为vector再按值降序排序,频次相同时按键升序;重建字符串时应避免单字符循环拼接,改用assign或重复构造提升效率。

用 std::map 统计频次再按值排序最直接
频次统计本身不能直接用 std::sort,因为字符串是线性容器,而排序依据是每个字符在其中的出现次数。必须先扫一遍字符串,把字符 → 次数映射建出来。用 std::map<char int></char> 最稳妥:自动去重、按键有序(虽然这里键序不重要),且支持 operator[] 累加。
注意别用 std::unordered_map 后直接遍历——它不保证遍历顺序,但后续排序逻辑不依赖容器遍历序,所以其实也能用;只是调试时输出可能“看起来乱”,容易误判统计出错。
- 空字符串或单字符输入要能处理(
map为空或只有一项,排序逻辑仍成立) - 区分大小写:
'A'和'a'算不同字符,如需忽略,提前用std::tolower转换 - 中文字符?
char不够,得用std::u8string+std::map<char32_t int></char32_t>,但这是进阶场景,基础题默认 ASCII
排序时不能对 map 本身按 value 排,得转成 vector
std::map 只能按键(key)排序,无法按值(value)重排。常见错误是试图写 std::sort(mymap.begin(), mymap.end(), ...) —— 编译失败,因为 map::iterator 是 const 的,且底层红黑树结构不支持随机访问迭代器。
正确做法:把 map 的每对 std::pair<const char int></const> 拷进 std::vector,再对 vector 排序:
立即学习“C++免费学习笔记(深入)”;
std::vector<std::pair<char, int>> vec(mymap.begin(), mymap.end());
std::sort(vec.begin(), vec.end(), [](const auto& a, const auto& b) {
return a.second > b.second; // 降序:高频在前
});
- 若频次相同,希望按字符 ASCII 升序稳定排序,改成
a.second != b.second ? a.second > b.second : a.first - 用
auto避免写冗长类型,但确保 C++14 或更高 - 别漏掉
>——默认升序,而“高频在前”需要降序
重建字符串时注意重复拼接的开销
拿到排序后的 vec,逐个字符重复 n 次追加到结果串里。最容易踩的坑是用 += 循环拼接单字符:
// ❌ 低效:每次 += 触发内存重分配
std::string result;
for (const auto& p : vec) {
for (int i = 0; i < p.second; ++i) result += p.first;
}
更优做法是预估总长(原字符串长度),用 reserve(),或直接用 std::string(p.first, p.second) 构造子串再拼:
// ✅ 高效且清晰
std::string result;
result.reserve(original.size()); // 避免多次扩容
for (const auto& p : vec) {
result += std::string(p.second, p.first); // 构造重复字符串
}
-
std::string(count, ch)是 O(count) 构造,比循环+=少很多中间对象 - 如果原字符串极大(比如 MB 级),要考虑是否真需要完整新字符串,还是只需输出/流式处理
完整流程里最容易被忽略的是稳定性和等频字符顺序
题目说“按字符出现频率排序”,但没说频率相同时怎么排。实际业务中,用户常默认“频率相同则保持原始首次出现顺序”(稳定排序),但 std::sort 是不稳定算法,且我们用的是 map→vector→sort,原始位置信息已丢失。
若需稳定,必须额外记录首次索引。简单办法:不用 map,改用 vector 扫描时记录 {char, count, first_index},用 std::stable_sort,按 count 降序主键、first_index 升序次键。但这会增加空间和逻辑复杂度。
多数场景下,按字符 ASCII 序补排序就够了——明确写在比较 lambda 里,别指望默认行为。


















