应使用 std::unordered_map 统计单词频次,遍历字符用 std::isalnum() 提取单词并转小写;需按频次排序时导出至 std::vector 后用 std::sort 和 lambda 自定义比较。

用 std::map 或 std::unordered_map 统计单词频次
核心是把字符串切分成单词,再逐个计数。C++ 没有内置的“按空格/标点切分”函数,得手动处理——别直接用 std::stringstream 配合 operator>>,它会跳过所有空白符(包括换行、制表符),但对标点不敏感,比如 "hello,world" 会被当做一个词。更稳妥的做法是遍历字符,用 std::isalnum() 判断是否为字母或数字,累积成单词;遇到非字母数字字符就提交当前单词(如果非空)。注意大小写:通常要转小写再统计,用 std::tolower 逐字符转换。
选 std::map 还是 std::unordered_map?如果只做频次统计,后者平均 O(1) 插入更快;但如果后续要按单词字典序输出,std::map 天然有序,省去排序开销。实际中多数场景用 std::unordered_map<:string int></:string> 更合理。
把频次映射转成可排序的容器(如 std::vector)
std::map 和 std::unordered_map 都不支持按 value(即频次)排序,必须导出到支持自定义比较的容器里。最常用的是 std::vector<:pair int>></:pair>,然后用 std::sort 配合 lambda。
常见错误:写成 sort(v.begin(), v.end(), [](auto a, auto b) { return a.second ,这会导致频次相同时顺序不确定(不稳定),且默认是升序——而高频词通常要排前面。应显式指定降序,并在频次相等时按单词升序避免歧义:
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
<h3>处理标点和边界情况的实际切分逻辑</h3>
<p>真实文本里单词常被逗号、句号、括号等包围,比如 <code>"Hello, world! How are you?"</code>。不能只靠空格分割,否则得到 <code>"Hello,"</code> 和 <code>"world!"</code> 这样的“脏词”。推荐做法:遍历每个字符,遇到 <code>std::isalnum(c)</code> 就加入当前单词;否则,若当前单词非空,就存入 map 并清空。注意结尾单词容易遗漏——循环结束后要检查一次 <code>current_word</code> 是否为空。</p><div class="aritcle_card flexRow artxards">
<div class="artcardd flexRow">
<a class="aritcle_card_img" rel="nofollow" href="/xiazai/skill5502" title="C++ Code Review Master"><img
src="https://img.php.cn/upload/skill/000/000/081/179051228971575.jpg" alt="C++ Code Review Master" onerror="this.onerror='';this.src='/static/lhimages/moren/morentu.png'" ></a>
<div class="aritcle_card_info flexColumn">
<a rel="nofollow" href="/xiazai/skill5502" title="C++ Code Review Master" class="overflowclass">C++ Code Review Master</a>
<p class="overflowclass">组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。</p>
</div>
<a rel="nofollow" href="/xiazai/skill5502" title="C++ Code Review Master" class="aritcle_card_btn flexRow flexcenter"><b></b><span>下载</span>
</a>
</div>
</div>
<p>其他易忽略点:</p>
- 空字符串或全空白字符串:提前返回,避免后续逻辑崩溃
- 连续多个标点(如
"a...b"):中间的"..."不产生单词,没问题 - Unicode?标准
std::isalnum只处理 ASCII,中文等需用 ICU 库,一般需求下暂不考虑
完整流程中的性能与内存提醒
整个流程时间复杂度约 O(N + M log M),N 是字符总数,M 是不同单词数。瓶颈往往不在排序,而在字符串切分和哈希计算——特别是长单词频繁调用 std::hash<:string></:string>。如果单词极多(如百万级唯一词),std::unordered_map 的哈希冲突可能拖慢,可考虑预留容量:word_count.reserve(expected_unique_count)。
另一个隐藏成本:每次插入 std::string 都涉及内存分配。若原始文本已存在(比如读自文件),可用 std::string_view(C++17 起)避免拷贝,但要注意生命周期——必须确保源字符串比 string_view 活得久。实践中,除非明确 profiling 发现拷贝是瓶颈,否则优先用 std::string 保证安全。
真正麻烦的不是算法,而是怎么定义“单词”:连字符("state-of-the-art")、撇号("don't")、数字混合("v2.1.0")……这些都需要根据具体需求调整判断逻辑,没有银弹。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!










