给你一个字符串 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)
额外空间复杂度的 原地 解法。
📺 视频题解
📖 文字题解 方法一:使用语言特性 思路和算法
很多语言对字符串提供了 split
(拆分),reverse
(翻转)和 join
(连接)等方法,因此我们可以简单的调用内置的 API 完成操作:
使用 split
将字符串按空格分割成字符串数组;
使用 reverse
将字符串数组进行反转;
使用 join
方法将字符串数组拼成一个字符串。
[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 (' ' ); };
复杂度分析
方法二:自行编写对应的函数 思路和算法
我们也可以不使用语言中的 API,而是自己编写对应的函数。在不同语言中,这些函数实现是不一样的,主要的差别是有些语言的字符串不可变(如 Java 和 Python),有些语言的字符串可变(如 C++)。
对于字符串不可变的语言,首先得把字符串转化成其他可变的数据结构,同时还需要在转化的过程中去除空格。
对于字符串可变的语言,就不需要再额外开辟空间了,直接在字符串上原地实现。在这种情况下,反转字符和去除空格可以一起完成。
[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 = 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 = 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] != ' ' ) { 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 = end; } } s.erase (s.begin () + idx, s.end ()); return s; } };
复杂度分析
方法三:双端队列 思路和算法
由于双端队列支持从队列头部插入的方法,因此我们可以沿着字符串一个一个单词处理,然后将单词压入队列的头部,再将队列转成字符串即可。
[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(), [] 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 == ' ' )) { 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 == ' ' ) { 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; } };
复杂度分析