必须用双指针而非std::reverse直接反转,因为后者会使单词内部字母翻转;双指针法通过整体反转、单词内反转、清理多余空格三步实现单词顺序反转且单词内字符不变,空间复杂度O(1)。
为什么要用双指针而不是
直接对整个字符串调用
会把单词内部字母也翻转,比如
变成
,而题目要的是单词顺序反转、单词内字符不变——即变成
。所以必须先按空格切分逻辑,再逐段反转,双指针法正好在原地完成这个过程,不依赖额外容器(如
),空间复杂度控制在
(不计输入字符串本身)。
双指针法三步走:整体反转 + 单词内反转 + 清理多余空格
标准做法是三个阶段,缺一不可,否则结果错乱:
第一遍:用两个指针从首尾向中间,
整个字符串 —— 让单词顺序倒过来,但每个单词也反了
第二遍:扫描字符串,对每个连续非空格子串(即一个单词),用局部双指针把它再翻回来
第三遍:处理开头、结尾和中间多个空格的问题;若要求“单词间仅一个空格”,需额外原地压缩(否则输出含冗余空格)
注意:C++ 中
支持随机访问和修改,所以所有操作可原地进行;但若传入的是
,需先拷贝一份可变副本。
原地反转单词顺序的完整实现
以下代码实现「单词顺序反转 + 单词内字符不变 + 单词间仅保留一个空格」,不含额外库依赖(除
和
):
立即学习
“
C++免费学习笔记(深入)
”;
AI Word
一款强大的 AI 智能内容创作平台,致力于帮助用户高效生成高质量、原创且符合 SEO 规范的各类文章。
下载
}
关键细节:
是双向迭代器安全的,适用于
;第三步中
指针控制实际写入位置,避免新建字符串;
截断末尾冗余字符。
容易被忽略的边界情况
很多实现在线 OJ 上失败,不是逻辑错,而是漏了这些:
输入为空字符串
或全空格(如
)—— 第三步压缩后应为
,不能 crash
单词间有多个连续空格(如
)—— 必须压缩,否则第二步翻转后仍残留空格,影响第三步判断
使用
做索引时,与负数比较(如
)会因无符号溢出导致死循环
调用
前未检查区间有效性(如
),虽通常安全,但在空单词逻辑里可能触发
最稳妥的做法是:所有索引用
,每步都校验
和
,压缩空格前先做
类清理(但会破坏原地性)—— 所以还是推荐上面的三阶段写法。
std::reversestd::reverse"hello world""dlrow olleh""world hello"std::vectorO(1)std::swapstd::stringconst std::string&std::stringvoid reverseWords(std::string& s) {
// 步骤1:整体反转
std::reverse(s.begin(), s.end());
// 步骤2:逐个单词反转(跳过空格,定位每个单词区间)
int n = s.length();
int i = 0;
while (i < n) {
if (s[i] != ' ') {
int j = i;
while (j < n && s[j] != ' ') j++;
std::reverse(s.begin() + i, s.begin() + j);
i = j;
} else {
i++;
}
}
// 步骤3:原地去重空格(保留单词间单空格,删首尾)
int write = 0;
for (int read = 0; read < n; ++read) {
if (s[read] != ' ') {
if (write != 0) s[write++] = ' '; // 单词间加空格(首个单词前不加)
while (read < n && s[read] != ' ') {
s[write++] = s[read++];
}
}
}
s.resize(write);std::reversestd::string::iteratorwrites.resize(write)""" """"a b"size_ti-- >= 0std::reversebegin() + i >= begin() + jinti < nj <= ns.erase(0, s.find_first_not_of(' '))