【探索-字节跳动】翻转字符串里的单词

在这里插入图片描述

解法一

实现思想,就是先整个的把字符串倒转过来,如 the sky is blue 直接倒转成 eulb si yks eht,然后,再单独把每个单词还原即可。另外还需要注意说明中的条件。

// 执行用时:4 ms
public String reverseWords(String s) {
    if (s==null||s.length()==0) return "";
    char[] chars = s.toCharArray();
    for (int i = 0, j = chars.length - 1; i < j; i++, j--) {
        char tmp = chars[i];
        chars[i] = chars[j];
        chars[j] = tmp;
    }

	// 将每个被反转的单词还原
    for (int i = 0; i < chars.length; i++) {
        if (chars[i]!=' ') {
            int j = i + 1;
            while (j < chars.length && chars[j] != ' ') ++j;
            --j;
            if (i != j) {
                for (int k = i,l = j; k <l ; k++,l--) {
                    char tmp = chars[k];
                    chars[k] = chars[l];
                    chars[l] = tmp;
                }
                i = j;
            }
        }
    }
    StringBuilder builder = new StringBuilder();
    // 用 left、rught 两个指针先排除两端的 0 元素
    int left = 0;
    // 要防止数组越界异常
    while (left<chars.length&&chars[left]== ' ') ++left;
    int right = chars.length - 1;
    while (right>=0&&chars[right]== ' ') --right;
    // 用一个标识位排除连续的多位空格
    boolean flag = true;
    for (; left <= right; left++) {
        char c = chars[left];
        if (c ==' '&& flag) {
            builder.append(c);
            flag = false;
        } else {
            builder.append(c);
            flag = true;
        }
    }
    return builder.toString();
}

或者是基于上述思想,但是对于空格的处理是基于 chars 进行原地操作。

// 执行用时:2 ms
public String reverseWords2(String s) {
    if (s == null || s.length() == 0) return "";
    char[] chars = s.toCharArray();
    for (int i = 0, j = chars.length - 1; i < j; i++, j--) {
        char tmp = chars[i];
        chars[i] = chars[j];
        chars[j] = tmp;
    }
    
    for (int i = 0; i < chars.length; i++) {
        if (chars[i] != ' ') {
            int j = i + 1;
            while (j < chars.length && chars[j] != ' ') ++j;
            --j;
            if (i != j) {
                for (int k = i, l = j; k < l; k++, l--) {
                    char tmp = chars[k];
                    chars[k] = chars[l];
                    chars[l] = tmp;
                }
                i = j;
            }
        }
    }
    int left = 0;
    int right = chars.length - 1;
    // 要防止数组越界异常
    while (left < chars.length && chars[left] == ' ') ++left;
    while (right > left && chars[right] == ' ') --right;
    
    // 将多个连续空格中多余的用之后的单词填充
    int i = left;
    while (i <= right) {
        if (chars[i] == ' ') {
            int tmp2 = i + 1;
            while (tmp2 <= right && chars[tmp2] == ' ') ++tmp2;
            // 如果有多个连续的空格的话
            if (tmp2 - i > 1) {
                ++i;//将 i 指向连续空格的第二个开始
                while (tmp2 <= right && chars[tmp2] != ' ') {
                    chars[i] = chars[tmp2];
                    chars[tmp2] = ' ';
                    // 表示到了末尾,所以直接缩小 right 的范围
                    if (tmp2 == right) {
                        right = i;
                    }
                    ++i;
                    ++tmp2;
                }
            } else {
                // 如果没有多个连续空格,也要继续向右移动 i,
                // 否则对于 chars[i] == ' ' && tmp2 - i <= 1时, i 一直会在原地不变
                ++i;
            }
        } else ++i;
    }
    return new String(chars, left, right - left + 1);
}

解法二

还有一种实现思路就是开辟一个辅助数组,从末尾遍历 s,边遍历,边倒转填充到辅助数组中。

// 执行用时:5 ms
public String reverseWords(String s) {
    if (s == null || s.length() == 0) return "";
    int len = s.length();
    int left = 0;
    int right = s.length() - 1;
    // 得到两边有效的起始边界
    while (left < len && s.charAt(left) == ' ') ++left;
    while (right >= 0 && s.charAt(right) == ' ') --right;
    // 这种情况就表示 s 中只有空格
    if (left > right) return "";
    // 直接开辟辅助数组去存储结果
    char[] chars = new char[right - left + 1];
    int i = 0;
    boolean flag = true;
    while (true) {
        if (right < left) break;
        // 针对 s 从末尾向前检测,如果检测到有空格或者 在 到来左边有效开始边界时
        // 当 right != left 时,
        // 需要注意的是 s.charAt(right) == ' ' 与 flag 不能同时判断,
        // 如 s.charAt(right) == ' ' && flag
        // 因为这样 flag 为 false 时会直接进入到 1 的 else 逻辑,将 flag 重置为 false
        // 此时如果对于对于的空格就会导致 s.charAt(right) == ' ' && flag 成立了
        if (s.charAt(right) == ' ' || right == left) {
            if (flag || right == left) {
                flag = false;
                // 如果是在到了左边有效边界,那就不需要 +1 而实现移动到该段单词的首个字母
                // 否则 s.charAt(right) == ' ' 则需要 +1 移动到单词的首个字母
                int j = (right == left) ? right : right + 1;
                for (; j < len && s.charAt(j) != ' '; j++) {
                    chars[i++] = s.charAt(j);
                }
                if (right != left) chars[i++] = ' ';
            }
        } else { // 1
            flag = true;
        }
        --right;
    }
    return new String(chars, 0, i);
}

解法三

是解法二的递归实现,代码很简洁,参考自提交记录 执行用时为 1 ms 的范例

public static String reverseWords(String s) {
    if (s==null||s.length()==0) return s;
    char[] array = s.toCharArray();
    char[] res = new char[array.length];
    int len = helper(array, array.length - 1, res, 0, 0);
    return new String(res, 0, len);
}
private int helper(char[] array, int r, char[] res, int l, int len) {
    // 从后面往前遍历时,先排除空格
    while (r >= 0 && array[r] == ' ') r--;
    if (r < 0) return Math.max(0, len - 1);
    int right = r;
    // 得到该段单词的起始位置
    while (r >= 0 && array[r] != ' ') r--;
    // right-r 是该段单词的长度, +1 是加上一个空格的长度
    // 因此在最后在末尾会多了一个空格
    // 所以在递归 return 的时候是 len-1 不包括这个多余的空格
    len += right - r + 1;
    for (int left = r + 1; left <= right; left++, l++)
        res[l] = array[left];
    if (l < res.length)
        res[l++] = ' ';
    return helper(array, r, res, l, len);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值