加载中...
151-翻转字符串里的单词(Reverse Words in a String)
发表于:2021-12-03 | 分类: 中等
字数统计: 563 | 阅读时长: 2分钟 | 阅读量:

原文链接: https://leetcode-cn.com/problems/reverse-words-in-a-string

英文原文

Given an input string s, reverse the order of the words.

A word is defined as a sequence of non-space characters. The words in s will be separated by at least one space.

Return a string of the words in reverse order concatenated by a single space.

Note that s may contain leading or trailing spaces or multiple spaces between two words. The returned string should only have a single space separating the words. Do not include any extra spaces.

 

Example 1:

Input: s = "the sky is blue"
Output: "blue is sky the"

Example 2:

Input: s = "  hello world  "
Output: "world hello"
Explanation: Your reversed string should not contain leading or trailing spaces.

Example 3:

Input: s = "a good   example"
Output: "example good a"
Explanation: You need to reduce multiple spaces between two words to a single space in the reversed string.

Example 4:

Input: s = "  Bob    Loves  Alice   "
Output: "Alice Loves Bob"

Example 5:

Input: s = "Alice does not even like bob"
Output: "bob like even not does Alice"

 

Constraints:

  • 1 <= s.length <= 104
  • s contains English letters (upper-case and lower-case), digits, and spaces ' '.
  • There is at least one word in s.

 

Follow-up: If the string data type is mutable in your language, can you solve it in-place with O(1) extra space?

中文题目

给你一个字符串 s ,逐个翻转字符串中的所有 单词

单词 是由非空格字符组成的字符串。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"
解释:如果两个单词间有多余的空格,将翻转后单词间的空格减少到只含一个。

示例 4:

输入:s = "  Bob    Loves  Alice   "
输出:"Alice Loves Bob"

示例 5:

输入:s = "Alice does not even like bob"
输出:"bob like even not does Alice"

 

提示:

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

 

进阶:

  • 请尝试使用 O(1) 额外空间复杂度的原地解法。

通过代码

高赞题解

📺 视频题解

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

📖 文字题解

方法一:使用语言特性

思路和算法

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

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

fig

[sol1-Python3]
class Solution: def reverseWords(self, s: str) -> str: return " ".join(reversed(s.split()))
[sol1-Java]
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]
var reverseWords = function(s) { return s.trim().split(/\s+/).reverse().join(' '); };

复杂度分析

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

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

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

思路和算法

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

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

fig

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

fig

[sol2-Python3]
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]
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++]
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]
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]
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++]
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)$ 的空间。

统计信息

通过次数 提交次数 AC比率
184489 378016 48.8%

提交历史

提交时间 提交结果 执行时间 内存消耗 语言

相似题目

题目 难度
翻转字符串里的单词 II 中等
上一篇:
150-逆波兰表达式求值(Evaluate Reverse Polish Notation)
下一篇:
152-乘积最大子数组(Maximum Product Subarray)
本文目录
本文目录