用std::sort配合自定义比较函数是最直接做法:先用unordered_map统计频次,再通过lambda捕获频次表排序;需避免原串修改导致频次错乱,推荐新建字符串并reserve优化性能。

用 std::sort 配合自定义比较函数是最直接的做法
核心思路是先统计每个字符出现次数,再按频次从高到低重排原字符串。不能直接对字符串排序,得先建频次映射,再构造新字符串或就地重排。
常见错误是把 std::sort 的比较函数写成按字符 ASCII 值排序,或者没处理频次相同时的稳定性(比如相同频次的字符顺序是否保留原序不重要,但必须明确行为)。
- 统计频次推荐用
std::unordered_map<char int></char>,O(n) 时间 - 比较函数必须捕获频次表(建议用 lambda + 引用捕获),否则每次调用都查不到数据
- 注意:如果原字符串含空格、标点或大小写混合,
char作为 key 是安全的;但不要用std::map替代unordered_map,除非你真需要有序遍历——这里不需要
处理频次相同时的字符顺序
题目只要求“按频率降序”,没说频次相同时怎么排。实际中常有两种需求:保持原始相对顺序(稳定排序),或按字符本身升序/降序补序。C++ std::sort 本身不稳定,且不保证相等元素顺序。
- 若需稳定:改用
std::stable_sort,但前提是你的比较函数严格弱序 —— 即频次相等时返回false(不能返回true或随机值),否则行为未定义 - 若频次相等时希望按 ASCII 升序排列:比较函数里加一层判断,如
freq[a] != freq[b] ? freq[a] > freq[b] : a - 频次相等时完全不管顺序?那就只比频次,
freq[a] > freq[b]即可 —— 但要注意:这会导致相等频次的字符顺序不确定,取决于std::sort内部实现
避免修改原字符串却忽略 const 正确性
如果你写的是一个函数,输入是 const std::string& s,那就别试图就地修改 s。常见错误是声明形参为 std::string s(拷贝)却在函数内对它调用 sort(s.begin(), s.end(), ...),结果返回时发现逻辑错乱 —— 因为排序依据的是旧频次表,而字符串已被打乱。
- 正确做法:先统计,再新建字符串,按频次+字符生成;或用索引数组排序后重排
- 错误示范:
std::sort(s.begin(), s.end(), [...](char a, char b){ return freq[a] > freq[b]; });—— 此时s已变,freq却基于原串统计,a/b 对应的频次查出来就错了 - 简单安全方案:统计完,遍历频次表,按频次降序拼接字符(每个字符重复对应次数),最后再按需排序同频字符
性能敏感场景下慎用多次 push_back
如果字符串很长(比如几万字符),频繁 push_back 单个字符会触发多次内存重分配。不是 bug,但影响实测性能。
- 提前 reserve:算出总长度(就是原串 size),调用
result.reserve(s.size()) - 更优:用
std::vector<:pair char>></:pair>存(频次,字符),排序后用std::string(count, c)批量构造子串再 append - 别用
+=拼接单字符,它和push_back效果一样;但+=拼接字符串字面量或std::string时,现代标准库通常能优化
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











