题目
解题思路
直接在原字符串上进行操作,只要将整个字符串都反转过来,那么字符串的顺序就倒序了,只不过单词本身也倒序了,那么再把单词反转一下,这道题目就完成了。所以这道题共分为三步:
- 移除多余的空格
- 反转整个字符串
- 反转每个单词
「移除多余的空格」的操作可以利用双指针直接在原字符串上来实现,将时间复杂度降到 O ( n ) O(n) O(n),「反转字符串」的操作也可以直接在原字符串上进行操作,时间复杂度也为 O ( n ) O(n) O(n),有些语言的字符串不可变(如 Java 和 Python),有些语言的字符串可变(如 C++),对于字符串不可变的语言,首先得把字符串转化成其他可变的数据结构,对于字符串可变的语言,就不需要再额外开辟空间了,直接在字符串上原地实现。
「移除多余的空格」的双指针思路如下:定义两个指针slow
与fast
初始化都为0,首先fast
后移直到指向第一个非空字符,随后执行s[slow++]=s[fast++]
将字符前移,填去空格,为了去除多余的空格,当fast
指针遇到两个相邻的空格时,跳过当前空格继续后移,移除多余空格后的结果如下图:
C++
class Solution {
public:
string reverseWords(string s) {
// 1. 移除多余空格
int len = s.length();
int fast = 0, slow = 0;
// 移除字符串之前的空格
while (s[fast] == ' ') {
fast++;
}
// 移除字符串里多余空格
while (fast < len - 1) {
if (s[fast] == ' ' && s[fast + 1] == ' ') {
fast++;
}
else {
s[slow++] = s[fast++];
}
}
if (s[fast] != ' ') {
s[slow++] = s[fast];
}
// 2. 反转整个字符串
reverse(s, 0, slow - 1);
// 3. 反转每个单词
for (int i = 0; i < slow; i++) {
int j = i;
while (j < slow && s[j] != ' ') {
j++;
}
reverse(s, i, j - 1);
i = j;
}
// 截断
return s.substr(0, slow);
}
void reverse(string& s, int start, int end) {
while (start < end) {
char temp = s[start];
s[start++] = s[end];
s[end--] = temp;
}
}
};
注意C++里string
和char*
对\0
的区别,char*
字符串 以 \0
结尾,string
字符串 不以 \0
结尾
void reverse(char* s, int start, int end) {
while (start < end) {
char temp = s[start];
s[start++] = s[end];
s[end--] = temp;
}
}
char * reverseWords(char * s){
// 1. 移除多余空格
int len = strlen(s);
int fast = 0, slow = 0;
// 移除字符串之前的空格
while (s[fast] == ' ') {
fast++;
}
// 移除字符串里多余的空格
while (fast < len - 1) {
if (s[fast] == ' ' && s[fast + 1] == ' ') {
fast++;
} else {
s[slow++] = s[fast++];
}
}
if (s[fast] == ' ') {
s[slow] = '\0';
} else {
s[slow++] = s[fast];
s[slow] = '\0';
}
// 2. 反转整个字符串
reverse(s, 0, slow - 1);
// 3. 反转每个单词
for (int i = 0; i < slow; i++) {
int j = i;
while (j < slow && s[j] != ' ') {
j++;
}
reverse(s, i, j - 1);
i = j;
}
return s;
}
Java
class Solution {
public void reverse(StringBuilder sb, int start, int end) {
while (start < end) {
char tmp = sb.charAt(start);
sb.setCharAt(start++, sb.charAt(end));
sb.setCharAt(end--, tmp);
}
}
public String reverseWords(String s) {
int len = s.length();
int slow = 0, fast = 0;
StringBuilder sb = new StringBuilder(s);
// 去除前导0
while (sb.charAt(fast) == ' ') {
fast++;
}
// 字符串往前移位,覆盖多余的空格
while (fast < len - 1) {
if (sb.charAt(fast) == ' ' && sb.charAt(fast + 1) == ' ') {
fast++;
} else {
sb.setCharAt(slow++, sb.charAt(fast++));
}
}
if (sb.charAt(fast) != ' ') {
sb.setCharAt(slow++, sb.charAt(fast));
}
// 反转整个字符串
reverse(sb, 0, slow - 1);
// 反转字符串里的单词
for (int i = 0; i < slow; i++) {
int j = i;
while (j < slow && sb.charAt(j) != ' ') {
j++;
}
reverse(sb, i, j - 1);
i = j;
}
return sb.substring(0, slow);
}
}
复杂度分析
-
时间复杂度: O ( n ) O(n) O(n),其中 n n n 为输入字符串的长度。
-
空间复杂度:Java 方法需要 O ( n ) O(n) O(n) 的空间来存储字符串,而 C++ 方法只需要 O ( 1 ) O(1) O(1) 的额外空间来存放若干变量。