问题

给定一个字符串,逐个翻转字符串中的每个单词

说明:

  • 无空格字符构成一个单词
  • 输入字符串可以在前面或者后面包含多余的空格,但是反转后的字符不能包括
  • 如果两个单词间有多余的空格,将反转后单词间的空格减少到只含一个


    示例 1:
    输入:”the sky is blue”
    输出:”blue is sky the”

示例 2:
输入:” hello world! “
输出:”world! hello”
解释:输入字符串可以在前面或者后面包含多余的空格,但是反转后的字符不能包括

示例 3:
输入:”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”

方法一:Java语言特性

  1. class Solution {
  2. public String reverseWords(String s) {
  3. // trim() 方法用于删除字符串的头尾空白符
  4. s = s.trim();
  5. // 正则匹配连续的空白字符作为分隔符分割
  6. // List<String>不能直接实例化
  7. /**split("\\s+")按空格、制表符等进行拆分(也就是说它是按空白部分进行拆分,不管这个空白使用什么 操作留下的,提如空格键 tab键)
  8. split(" +") 按空格进行拆分(也就是说只有按空格键流出来的空白才会是拆分的一句)
  9. \\s表示 空格,回车,换行等空白符,+号表示一个或多个的意思
  10. */
  11. List<String> wordList = Arrays.asList(s.split("\\s+")); // 将数组转化成list集合
  12. /**
  13. sd@sdd@sddd
  14. split("@")
  15. [sd,sdd,sddd]
  16. */
  17. Collections.reverse(wordList);
  18. return String.join(" ", wordList);
  19. }
  20. }
  • 时间复杂度:leetcode-151:翻转字符串里的单词 - 图1,其中 N 为输入字符串的长度

  • 空间复杂度:leetcode-151:翻转字符串里的单词 - 图2,用来存储字符串分割之后的结果

方法二

  1. class Solution {
  2. public String reverseWords(String s) {
  3. StringBuilder sb = trimSpaces(s);
  4. // 翻转字符串
  5. reverse(sb, 0, sb.length() - 1);
  6. // 翻转每个单词
  7. reverseEachWord(sb);
  8. return sb.toString();
  9. }
  10. public StringBuilder trimSpaces(String s) {
  11. int left = 0, right = s.length() - 1;
  12. // 去掉字符串开头的空白字符
  13. while (left <= right && s.charAt(left) == ' ') {
  14. ++left;
  15. }
  16. // 去掉字符串末尾的空白字符
  17. while (left <= right && s.charAt(right) == ' ') {
  18. --right;
  19. }
  20. // 将字符串间多余的空白字符去除
  21. StringBuilder sb = new StringBuilder();
  22. while (left <= right) {
  23. char c = s.charAt(left);
  24. if (c != ' ') {
  25. sb.append(c);
  26. } else if (sb.charAt(sb.length() - 1) != ' ') {
  27. sb.append(c);
  28. }
  29. ++left;
  30. }
  31. return sb;
  32. }
  33. public void reverse(StringBuilder sb, int left, int right) {
  34. while (left < right) {
  35. char tmp = sb.charAt(left);
  36. //setCharAt(int index,Char ch),第一个参数是取代的位置索引从0开始,第二个参数是你要替换 为的字符串
  37. sb.setCharAt(left++, sb.charAt(right));
  38. sb.setCharAt(right--, tmp);
  39. }
  40. }
  41. public void reverseEachWord(StringBuilder sb) {
  42. int n = sb.length();
  43. int start = 0, end = 0;
  44. while (start < n) {
  45. // 循环至单词的末尾
  46. while (end < n && sb.charAt(end) != ' ') {
  47. ++end;
  48. }
  49. // 翻转单词
  50. reverse(sb, start, end - 1);
  51. // 更新start,去找下一个单词
  52. start = end + 1;
  53. ++end;
  54. }
  55. }
  56. }
  • 时间复杂度:leetcode-151:翻转字符串里的单词 - 图3,其中 N 为输入字符串的长度

  • 空间复杂度:leetcode-151:翻转字符串里的单词 - 图4,N 为存储字符串的长度

方法三:双端队列

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

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);
    }
}