0151-反转字符串中的单词

Raphael Liu Lv10

给你一个字符串 s ,请你反转字符串中 单词 的顺序。

单词 是由非空格字符组成的字符串。s 中使用至少一个空格将字符串中的 单词 分隔开。

返回 单词 顺序颠倒且 单词 之间用单个空格连接的结果字符串。

注意: 输入字符串
s中可能会存在前导空格、尾随空格或者单词间的多个空格。返回的结果字符串中,单词间应当仅用单个空格分隔,且不包含任何额外的空格。

示例 1:

**输入:** s = "the sky is blue"
**输出:** "blue is sky the"

示例 2:

**输入:** s = "  hello world  "
**输出:** "world hello"
**解释:** 反转后的字符串中不能存在前导空格和尾随空格。

示例 3:

**输入:** s = "a good   example"
**输出:** "example good a"
**解释:** 如果两个单词间有多余的空格,反转后的字符串需要将单词间的空格减少到仅有一个。

提示:

  • 1 <= s.length <= 104
  • s 包含英文大小写字母、数字和空格 ' '
  • s至少存在一个 单词

进阶: 如果字符串在你使用的编程语言中是一种可变数据类型,请尝试使用 O(1) 额外空间复杂度的 原地 解法。

📺 视频题解

151. 翻转字符串里的单词.mp4

📖 文字题解

方法一:使用语言特性

思路和算法

很多语言对字符串提供了 split(拆分),reverse(翻转)和 join(连接)等方法,因此我们可以简单的调用内置的 API 完成操作:

  1. 使用 split 将字符串按空格分割成字符串数组;
  2. 使用 reverse 将字符串数组进行反转;
  3. 使用 join 方法将字符串数组拼成一个字符串。

fig

[sol1-Python3]
1
2
3
class Solution:
def reverseWords(self, s: str) -> str:
return " ".join(reversed(s.split()))
[sol1-Java]
1
2
3
4
5
6
7
8
9
10
class Solution {
public String reverseWords(String s) {
// 除去开头和末尾的空白字符
s = s.trim();
// 正则匹配连续的空白字符作为分隔符分割
List<String> wordList = Arrays.asList(s.split("\\s+"));
Collections.reverse(wordList);
return String.join(" ", wordList);
}
}
[sol1-JavaScript]
1
2
3
var reverseWords = function(s) {
return s.trim().split(/\s+/).reverse().join(' ');
};

复杂度分析

  • 时间复杂度:$O(n)$,其中 $n$ 为输入字符串的长度。

  • 空间复杂度:$O(n)$,用来存储字符串分割之后的结果。

方法二:自行编写对应的函数

思路和算法

我们也可以不使用语言中的 API,而是自己编写对应的函数。在不同语言中,这些函数实现是不一样的,主要的差别是有些语言的字符串不可变(如 Java 和 Python),有些语言的字符串可变(如 C++)。

对于字符串不可变的语言,首先得把字符串转化成其他可变的数据结构,同时还需要在转化的过程中去除空格。

fig

对于字符串可变的语言,就不需要再额外开辟空间了,直接在字符串上原地实现。在这种情况下,反转字符和去除空格可以一起完成。

fig

[sol2-Python3]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
class Solution:
def trim_spaces(self, s: str) -> list:
left, right = 0, len(s) - 1
# 去掉字符串开头的空白字符
while left <= right and s[left] == ' ':
left += 1

# 去掉字符串末尾的空白字符
while left <= right and s[right] == ' ':
right -= 1

# 将字符串间多余的空白字符去除
output = []
while left <= right:
if s[left] != ' ':
output.append(s[left])
elif output[-1] != ' ':
output.append(s[left])
left += 1

return output

def reverse(self, l: list, left: int, right: int) -> None:
while left < right:
l[left], l[right] = l[right], l[left]
left, right = left + 1, right - 1

def reverse_each_word(self, l: list) -> None:
n = len(l)
start = end = 0

while start < n:
# 循环至单词的末尾
while end < n and l[end] != ' ':
end += 1
# 翻转单词
self.reverse(l, start, end - 1)
# 更新start,去找下一个单词
start = end + 1
end += 1

def reverseWords(self, s: str) -> str:
l = self.trim_spaces(s)

# 翻转字符串
self.reverse(l, 0, len(l) - 1)

# 翻转每个单词
self.reverse_each_word(l)

return ''.join(l)
[sol2-Java]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
class Solution {
public String reverseWords(String s) {
StringBuilder sb = trimSpaces(s);

// 翻转字符串
reverse(sb, 0, sb.length() - 1);

// 翻转每个单词
reverseEachWord(sb);

return sb.toString();
}

public StringBuilder trimSpaces(String s) {
int left = 0, right = s.length() - 1;
// 去掉字符串开头的空白字符
while (left <= right && s.charAt(left) == ' ') {
++left;
}

// 去掉字符串末尾的空白字符
while (left <= right && s.charAt(right) == ' ') {
--right;
}

// 将字符串间多余的空白字符去除
StringBuilder sb = new StringBuilder();
while (left <= right) {
char c = s.charAt(left);

if (c != ' ') {
sb.append(c);
} else if (sb.charAt(sb.length() - 1) != ' ') {
sb.append(c);
}

++left;
}
return sb;
}

public void reverse(StringBuilder sb, int left, int right) {
while (left < right) {
char tmp = sb.charAt(left);
sb.setCharAt(left++, sb.charAt(right));
sb.setCharAt(right--, tmp);
}
}

public void reverseEachWord(StringBuilder sb) {
int n = sb.length();
int start = 0, end = 0;

while (start < n) {
// 循环至单词的末尾
while (end < n && sb.charAt(end) != ' ') {
++end;
}
// 翻转单词
reverse(sb, start, end - 1);
// 更新start,去找下一个单词
start = end + 1;
++end;
}
}
}
[sol2-C++]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
class Solution {
public:
string reverseWords(string s) {
// 反转整个字符串
reverse(s.begin(), s.end());

int n = s.size();
int idx = 0;
for (int start = 0; start < n; ++start) {
if (s[start] != ' ') {
// 填一个空白字符然后将idx移动到下一个单词的开头位置
if (idx != 0) s[idx++] = ' ';

// 循环遍历至单词的末尾
int end = start;
while (end < n && s[end] != ' ') s[idx++] = s[end++];

// 反转整个单词
reverse(s.begin() + idx - (end - start), s.begin() + idx);

// 更新start,去找下一个单词
start = end;
}
}
s.erase(s.begin() + idx, s.end());
return s;
}
};

复杂度分析

  • 时间复杂度:$O(n)$,其中 $n$ 为输入字符串的长度。

  • 空间复杂度:JavaPython 的方法需要 $O(n)$ 的空间来存储字符串,而 C++ 方法只需要 $O(1)$ 的额外空间来存放若干变量。

方法三:双端队列

思路和算法

由于双端队列支持从队列头部插入的方法,因此我们可以沿着字符串一个一个单词处理,然后将单词压入队列的头部,再将队列转成字符串即可。

fig

[sol3-Python3]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Solution:
def reverseWords(self, s: str) -> str:
left, right = 0, len(s) - 1
# 去掉字符串开头的空白字符
while left <= right and s[left] == ' ':
left += 1

# 去掉字符串末尾的空白字符
while left <= right and s[right] == ' ':
right -= 1

d, word = collections.deque(), []
# 将单词 push 到队列的头部
while left <= right:
if s[left] == ' ' and word:
d.appendleft(''.join(word))
word = []
elif s[left] != ' ':
word.append(s[left])
left += 1
d.appendleft(''.join(word))

return ' '.join(d)
[sol3-Java]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
class Solution {
public String reverseWords(String s) {
int left = 0, right = s.length() - 1;
// 去掉字符串开头的空白字符
while (left <= right && s.charAt(left) == ' ') {
++left;
}

// 去掉字符串末尾的空白字符
while (left <= right && s.charAt(right) == ' ') {
--right;
}

Deque<String> d = new ArrayDeque<String>();
StringBuilder word = new StringBuilder();

while (left <= right) {
char c = s.charAt(left);
if ((word.length() != 0) && (c == ' ')) {
// 将单词 push 到队列的头部
d.offerFirst(word.toString());
word.setLength(0);
} else if (c != ' ') {
word.append(c);
}
++left;
}
d.offerFirst(word.toString());

return String.join(" ", d);
}
}
[sol3-C++]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
class Solution {
public:
string reverseWords(string s) {
int left = 0, right = s.size() - 1;
// 去掉字符串开头的空白字符
while (left <= right && s[left] == ' ') ++left;

// 去掉字符串末尾的空白字符
while (left <= right && s[right] == ' ') --right;

deque<string> d;
string word;

while (left <= right) {
char c = s[left];
if (word.size() && c == ' ') {
// 将单词 push 到队列的头部
d.push_front(move(word));
word = "";
}
else if (c != ' ') {
word += c;
}
++left;
}
d.push_front(move(word));

string ans;
while (!d.empty()) {
ans += d.front();
d.pop_front();
if (!d.empty()) ans += ' ';
}
return ans;
}
};

复杂度分析

  • 时间复杂度:$O(n)$,其中 $n$ 为输入字符串的长度。

  • 空间复杂度:$O(n)$,双端队列存储单词需要 $O(n)$ 的空间。

 Comments
On this page
0151-反转字符串中的单词