LC 151. 反转字符串中的单词

151. 反转字符串中的单词

给你一个字符串 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 . l e n g t h ≤ 1 0 4 1 \leq s.length \leq 10^4 1s.length104
  • s 包含英文大小写字母、数字和空格 ' '
  • s至少存在一个 单词

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

解法一(快慢双指针)

思路分析:

  1. 使用快慢双指针法,快慢指针从后往前遍历 如此先找到的单词则放在返回结果的前面 实现反转;
  2. 快指针指向一个单词的起始位置,慢指针则指向一个单词的末尾位置
  3. 每当找到一个单词后,则将单词保存到ans中,然后继续移动快慢指针
    实现代码如下:
class Solution {
    public String reverseWords(String s) {
        int n = s.length();        // 字符串s的长度
        if (n == 1)
            return s;    // 当只有一个单词时 无需反转
        StringBuffer ans = new StringBuffer();    // 记录反转后的字符串
        int slow = n-1;    // 慢指针 记录一个单词的末尾位置
        while (slow >= 0 && s.charAt(slow) == ' ')
            -- slow;    // 跳过后置空格 并指向第一个单词的末尾位置
        int fast = slow;    // 快指针 指向单词的起始位置
        while (fast >= 0) {
            // 移动快指针到 一个单词的起始
            while (fast >= 0 && s.charAt(fast) != ' ')
                -- fast;
            // 找到一个单词 则将单词保存到返回值中
            ans.append(s, fast+1, slow+1).append(' ');
            // 继续移动慢指针 到下一个单词的末尾位置
            slow = fast;
            while (slow >= 0 && s.charAt(slow) == ' ')
                -- slow;
            fast = slow;    // 更新快指针下一次移动的起始位置
        }
        ans.deleteCharAt(ans.length()-1);    // 删除末尾多余的空格
        return new String(ans);
    }
}

提交结果如下:

解答成功:
    执行耗时:3 ms,击败了90.82% 的Java用户
    内存消耗:40.6 MB,击败了73.99% 的Java用户

复杂度分析:

  • 时间复杂度: O ( n ) O(n) O(n),n为字符串s的长度,快慢指针一共遍历字符串一次,不包括ans.append(s, fast+1, slow+1)的时间复杂度
  • 空间复杂度: O ( 1 ) O(1) O(1),不计算返回值所消耗的空间,则双指针花费的空间复杂度为 O ( 1 ) O(1) O(1)

解法二(不使用Java内置函数)

思路分析:

  1. 先去除字符串前后空格和中间多余的空格,参考移除元素,使用快慢双指针法
  2. 然后反转整个字符串 使用相向双指针进行反转
  3. 最后反转每个单词
  4. 使用s.toCharArray()获取一个可修改的字符数组,在该数组的基础上进行修改
    实现代码如下:
class Solution {
    public String reverseWords(String s) {
        int n = s.length();        // 字符串s的长度
        if (n == 1)
            return s;    // 当只有一个单词时 无需反转
        char[] str = s.toCharArray();    // 将字符串转化为字符数组
        str = removeExtraSpaces(str);    // 去除多余的空格
        // 反转整个字符串
        reverseString(0, str.length-1, str);
        // 反转新字符串数组中的 每个单词
        int start = 0;    // 单词的起始索引
        for (int end = 0; end <= str.length; ++end) {
            if (end == str.length || str[end] == ' ') {    // end==str.length时 反转末尾的单词
                reverseString(start, end-1, str);
                start = end + 1;
            }
        }
        return new String(str);
    }

    // 相向双指针 反转字符串
    private void reverseString(int left, int right, char[] str) {
        while (left < right) {
            // 交换左右指针 指向的数组元素
            str[left] ^= str[right];
            str[right] ^= str[left];
            str[left] ^= str[right];
            // 移动左右指针
            ++ left;
            -- right;
        }
    }

    // 去除字符串 前后及中间的多余空格
    private char[] removeExtraSpaces(char[] str) {
        // 使用快慢双指针法
        int n = str.length;        // 数组长度
        int slow = 0;
        for (int fast = 0; fast < str.length; ++fast) {
            if (str[fast] != ' ') {    // 快指针开始遍历一个单词
                if (slow != 0) {    // 若此时遍历的单词不是第一个单词 则需要用空格隔开
                    str[slow++] = ' ';
                }
                while (fast < n && str[fast] != ' ')    // 快指针遍历到一个单词的末尾
                    str[slow++] = str[fast++];
            }
        }
        // 去除多余空格后的数组 长度为 slow
        // 因此需要返回新的数组
        char[] newStr = new char[slow];
        System.arraycopy(str, 0, newStr, 0, slow);
        return newStr;
    }
}

提交结果如下:

解答成功:
    执行耗时:2 ms,击败了97.62% 的Java用户
    内存消耗:40.7 MB,击败了66.51% 的Java用户

复杂度分析:

  • 时间复杂度: O ( n ) O(n) O(n),移除多余空格和反转字符串的时间复杂度均为 O ( n ) O(n) O(n)
  • 空间复杂度: O ( n ) O(n) O(n),因为需要改变字符串长度,需要使用新的数组,所以消耗空间为 O ( n ) O(n) O(n)
  • 32
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值