151. 反转字符串中的单词
151. 反转字符串中的单词
1. 移除多余空格并重构字符串
这部分代码的作用是原地删除字符串里的多余空格,使得每个单词之间只有一个空格,并去除字符串首尾的多余空格。代码如下:
int n = s.size();
int slow = 0;
for (int fast = 0; fast < n; fast++) {
if (s[fast] != ' ') { // 遇到非空格字符,说明遇到了一个单词的开始
if (slow != 0)
s[slow++] = ' '; // 如果不是第一个单词,在复制新单词前先在结果里添加一个空格
while (fast < n && s[fast] != ' ') { // 复制这个单词(字符序列),直到遇到空格结束
s[slow] = s[fast];
slow++;
fast++;
}
}
}
s.resize(slow);
详细说明:
-
变量定义
-
n
为原始字符串长度。 -
fast
:快速指针,用来遍历原字符串。 -
slow
:慢速指针,用于写入处理后的字符,最开始为 0。
-
-
外层
for
循环-
遍历字符串中每一个字符(
fast
从 0 到 n-1)。 -
当
s[fast]
是非空格字符时,说明找到了一个单词的开始。
-
-
插入空格的处理
-
if (slow != 0)
判断当前写入位置不在开头,则在新的字符串中加入一个空格。这样可以确保不同单词之间只有一个空格,并且第一个单词前不会多余空格。
-
-
复制单词
-
内层的
while
循环把当前单词从原字符串复制到s[slow]
。 -
当遇到空格或者到达末尾时,停止复制,单词就被完整复制到了前面的位置。
-
注意:由于内层循环中已经把
fast
后移,所以外层循环之后,fast
会继续读取剩下的字符。
-
-
调整字符串大小
-
s.resize(slow);
将字符串裁剪到实际写入的字符数,这样会去除尾部原字符串剩余的字符。
-
2. 反转整个字符串
reverse(s.begin(), s.end());
这一步将整个字符串进行反转。因为我们经过第一步得到的字符串中每个单词之间只有一个空格,整体反转后,单词的顺序也反转了,但单词内的字符顺序同时也反转了。例如:
原字符串(去掉多余空格后)是 "the sky is blue"
反转后变成 "eulb si yks eht"
3. 恢复每个单词的正确顺序
反转整个字符串后,每个单词内部的字母顺序也被反转了。为了使单词看起来正常,我们需要再把每个单词单独反转回来。
代码如下:
int index = 0;
for (int i = 0; i < s.size(); i++) {
if (s[i] == ' ') {
reverse(s.begin() + index, s.begin() + i); // 翻转 [index, i) 范围内的字符,即当前单词
index = i + 1; // 更新下一个单词的起点
}
}
reverse(s.begin() + index, s.end()); // 最后一个单词单独反转
详细说明:
-
变量
index
-
表示当前单词的起始位置(即上一空格后面的位置)。
-
-
遍历字符串
-
使用
for
循环遍历整个字符串,当遇到空格时,说明已经找到了一个完整单词的边界。
-
-
反转单词
-
对于每次遇到的空格,调用
reverse(s.begin() + index, s.begin() + i)
翻转[index, i)
区间内的字符,这正好是一个单词。 -
然后将
index
更新为i + 1
,即下个单词的起点。
-
-
处理最后一个单词
-
遍历结束后最后一个单词后面没有空格,所以循环内部没有处理它,因此需要在循环结束后,再调用
reverse(s.begin() + index, s.end());
翻转最后一个单词。
-
4. 返回最终结果
return s;
经过以上步骤,最终返回的字符串就是按照单词顺序反转后、且单词内部字符顺序正确、仅保留一个空格分隔的字符串。
总结
-
去除多余空格
-
利用快慢指针遍历字符串,原地复制单词并在单词之间插入单个空格。
-
-
整体反转字符串
-
将所有字符整体反转,这一步反转了单词的顺序,但单词内部也被反转。
-
-
反转每个单词
-
对每个单词分别再反转一次,使单词内部字符恢复正确顺序。
-
-
返回结果
-
得到的字符串即为“单词顺序反转且各单词间只有单个空格”的结果。
-
这样整个处理流程既实现了去除多余空格,又完成了单词顺序反转,同时保留了每个单词内部字符的正确顺序。