必须用双指针而非std::reverse直接反转,因为后者会使单词内部字母翻转;双指针法通过整体反转、单词内反转、清理多余空格三步实现单词顺序反转且单词内字符不变,空间复杂度O(1)。

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











