c++中如何实现字符串的按单词反转_c++双指针法反转逻辑【详解】

必须用双指针而非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 原地反转单词顺序的完整实现

以下代码实现「单词顺序反转 + 单词内字符不变

+ 单词间仅保留一个空格」,不含额外库依赖(除 ):

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(' ')) 类清理(但会破坏原地性)—— 所以还是推荐上面的三阶段写法。