算法 1.9.2 翻转字符串里的单词 II【leetcode 186】

题目描述

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

示例:
  输入: ["t","h","e"," ","s","k","y"," ","i","s"," ","b","l","u","e"]
  输出: ["b","l","u","e"," ","i","s"," ","s","k","y"," ","t","h","e"]

注意:
  单词的定义是不包含空格的一系列字符
  输入字符串中不会包含前置或尾随的空格
  单词与单词之间永远是以单个空格隔开的

进阶:
  使用 O(1) 额外空间复杂度的原地解法


数据结构

  • 字符串、字符数组

算法思维

  • 遍历、逆序、双指针

解题要点

  • 如何优化实现一个字符串中的单词顺序的反转


解题思路


一. Comprehend 理解题意
  • 每个单词内部字符的顺序不变
  • 单词顺序改变
  • 操作对象由 String 变成 char[]

二. Choose 选择数据结构与算法
暴力解法:双指针 + 新数组
  • 数据结构:指针、字符数组
  • 算法思维:遍历、逆序、双指针


三. Code 编码实现基本解法
class Solution {
    /*
     * 双指针 + 新数组
     * */
    public void reverseWords(char[] s) {

        int len = s.length;
        StringBuffer sb = new StringBuffer();

        int left = 0, right = len - 1;

        for (int i = len - 1; i >= 0; i--) {
            if (s[i] == ' ') {
                left = i + 1;
                while (left <= right) {
                    sb.append(s[left++]);
                }
                sb.append(' ');
                right = i - 1;
            }
            if (i == 0) {
                left = 0;
                while (left <= right) {
                    sb.append(s[left++]);
                }
            }
        }

        char[] chars = sb.toString().toCharArray();
        for (int j = 0; j < len; j++) {
            s[j] = chars[j];
        }
    }
}

执行耗时:3 ms,击败了 23.58% 的Java用户
内存消耗:47.2 MB,击败了 5.18% 的Java用户
时间复杂度:O(n) -- 两次字符数组的遍历 O(n),一次字符数组赋值 O(n)
空间复杂度:O(n) -- 一个缓冲区 O(n),一个新字符数组 O(n)


四. Consider 思考更优解
改变算法思维
  • 本题难点在于:如何反转单词在字符串中的顺序?
    可以先反转整个字符串,再逐个单词反转字符
    或者先逐个单词反转字符,再反转整个字符串


五. Code 编码实现最优解
优化解法:双翻法
class Solution {
    /*
     * 双翻法:先逐个单词翻转字符,再翻转整个字符串
     * */
    public void reverseWords(char[] s) {

        //0.非空判断
        if (s == null || s.length == 0) return;

        int len = s.length;
        int left = 0, right = 0; //左右指针

        //1.遍历字符数组
        while (right < len) {
            //读取到空格时,说明读了一个单词
            if (s[right] == ' ') {
                reverse(s, left, right - 1); //反转单词字母
                left = right + 1; //定位新的左指针
            } else if (right == len - 1) { //到达末尾时,读的是最后一个单词
                reverse(s, left, right); //反转最后一个单词的字母
            }
            right++;
        }

        //2.最终将整个字符串反转
        reverse(s, 0, len - 1);
    }

    //反转整个字符数组的方法
    private void reverse(char[] target, int from, int to) {
        while (from < to) {
            char c = target[from];
            target[from] = target[to];
            target[to] = c;
            from++;
            to--;
        }
    }

}

执行耗时:1 ms,击败了 99.59% 的Java用户
内存消耗:44.1 MB,击败了 83.62% 的Java用户
时间复杂度:O(n) -- 一次字符数组的遍历 O(n)
空间复杂度:O(1) -- 双指针 O(1)


六. Change 变形与延伸

=== 待续 ===

你可能感兴趣的:(算法 1.9.2 翻转字符串里的单词 II【leetcode 186】)