算法训练营第八天| 344. 反转字符串、541. 反转字符串 II、151. 反转字符串中的单词、剑指 Offer 58 - II. 左旋转字符串

130 阅读3分钟

344. 反转字符串

编写一个函数,其作用是将输入的字符串反转过来。输入字符串以字符数组 s 的形式给出。

不要给另外的数组分配额外的空间,你必须原地修改输入数组、使用 O(1) 的额外空间解决这一问题。

image.png

思路-双指针法

  • 申请两个指针leftrightleft指向数组第一位,right指向数组末位
  • 交换两个指针所对应的字母,同时两个指针同时向内移动:left++,right--
  • 重复这个流程直至left >= right

代码实现

class Solution {
    public void reverseString(char[] s) {
        int left = 0;
        int right = s.length - 1;
        while ( left < right) {
            char temp = s[left];
            s[left] = s[right];
            s[right] = temp;
            left++;
            right--;
        }
    }
}
  • 时间复杂度:O(n)
  • 空间复杂度: O(1)

541. 反转字符串 II

给定一个字符串 s 和一个整数 k,从字符串开头算起,每计数至 2k 个字符,就反转这 2k 字符中的前 k 个字符。

  • 如果剩余字符少于 k 个,则将剩余字符全部反转。
  • 如果剩余字符小于 2k 但大于或等于 k 个,则反转前 k 个字符,其余字符保持原样。

image.png

思路

其实在遍历字符串的过程中,只要让 i += (2 * k),i 每次移动 2 * k 就可以了,然后判断是否需要有反转的区间。

因为要找的也就是每2 * k 区间的起点,这样写,程序会高效很多。

代码实现

class Solution {
    public String reverseStr(String s, int k) {
        char[] ch = s.toCharArray();
        for (int i = 0; i < ch.length; i+= 2 * k) {
            // 如果剩余字符小于 2k 但大于或等于 k 个,则反转前 k 个字符,其余字符保持原样。
            if ( i + k <= ch.length) {
                reverseString(ch, i, i + k - 1);
            } else {
                //如果剩余字符少于 k 个,则将剩余字符全部反转。
                reverseString(ch, i, ch.length - 1);
            }
        }
        return new String(ch);
    }

    public void reverseString(char[] s, int left, int right) {
        while ( left < right) {
            char temp = s[left];
            s[left] = s[right];
            s[right] = temp;
            left++;
            right--;
        }
    }
}
  • 时间复杂度: O(n)
  • 空间复杂度: O(1)

参考文章

代码随想录:反转字符串Ⅱ

剑指 Offer 05. 替换空格

请实现一个函数,把字符串 s 中的每个空格替换成"%20"

image.png

思路-正常

这题很简单,创建一个新的StringBuilder对象,遍历整个 String,发现空格的时候StringBuilder对象改为"%20"即可。

代码实现-正常

class Solution {
    public String replaceSpace(String s) {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < s.length(); ++i) {
            if (s.charAt(i) == ' ') {
                sb.append("%20");
            } else {
                sb.append(s.charAt(i));
            }
        }
        return sb.toString();
    }
}
  • 时间复杂度: O(n)
  • 空间复杂度: O(n)

思路-双指针

使用双指针法

  • 首先扩容数组,空格为一个字符,“%20”为三个,因此每拥有一个空格,我们就需要扩容两个字符位置(%20 是三个字符,算上原空格一个 + 扩容的两个,正好三个)位置
  • 一个指针指向原字符串末尾,一个指针指向扩容后的字符串末尾
  • 左指针遇到空格,右指针就依次从后往前写“0”,“2”,“%”;其他情况左指针是什么,右指针就写什么。

代码实现-双指针法

class Solution {
    public String replaceSpace(String s) {
        if (s == null || s.length() == 0) return s;
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < s.length(); i++) {
            if (s.charAt(i) == ' ') {
                sb.append("  ");
            }
        }
        // 如果sb的长度为0,代表原字符串s种没有空格,可直接返回
        if (sb.length() == 0) return s;

        int left = s.length() - 1;
        s += sb.toString();
        int right = s.length() - 1;
        char[] ch = s.toCharArray();
        for (; left >= 0; --left, --right) {
            if (ch[left] == ' ') {
                ch[right--] = '0';
                ch[right--] = '2';
                ch[right] = '%';
            } else {
                ch[right] = ch[left];
            }
        }
        return new String(ch);
    }
}
  • 时间复杂度: O(n)
  • 空间复杂度: O(n)

151. 反转字符串中的单词

给你一个字符串 s ,请你反转字符串中 单词 的顺序。

单词 是由非空格字符组成的字符串。s 中使用至少一个空格将字符串中的 单词 分隔开。

返回 单词 顺序颠倒且 单词 之间用单个空格连接的结果字符串。

注意: 输入字符串 s中可能会存在前导空格、尾随空格或者单词间的多个空格。返回的结果字符串中,单词间应当仅用单个空格分隔,且不包含任何额外的空格。

image.png

思路

本体主要是考察我们对字符串的操作,没有什么特别大的难点。具体流程如下:

  • 首先将字符串首部和尾部的空格去除
  • 然后将中间连续的多个空格变为单个空格
  • 然后翻转反转字符串中 单词 的顺序

需要注意的是,本体是翻转单词顺序,而非字符串翻转
那么如何做到翻转单词顺序而不破坏单词自身的顺序呢?
举个例子:假设源字符串为:"the sky is blue "

  • 首先翻转字符串:"eulb si yks eht"
  • 然后再对单词进行翻转:"blue is sky the"

代码实现

class Solution {
    public String reverseWords(String s) {
        StringBuilder sb = removeSpace(s);
        int start = 0;
        reverseString(sb, 0 , sb.length() - 1);
        int n = sb.length();
        for (int end = 0; end <= n; ++end) {
            if (end == n || sb.charAt(end) == ' ' ) {
                reverseString(sb, start, end - 1);
                start = end + 1;
            } 
        }
        return sb.toString();
    }
    
    private StringBuilder removeSpace(String s) {
        int start = 0;
        int end = s.length() - 1;
        while (s.charAt(start) == ' ') start++;
        while (s.charAt(end) == ' ') end--;
        StringBuilder sb = new StringBuilder();
        for (int i = start; i <= end; ++i) {
            char c = s.charAt(i);
            if (i > start && c == ' ' && s.charAt(i - 1) == ' ') {
                continue;
            } else {
                sb.append(c);
            }
        }
        return sb;
    } 

    private void reverseString(StringBuilder sb, int start, int end){
        while (start < end) {
            char temp = sb.charAt(start);
            sb.setCharAt(start, sb.charAt(end));
            sb.setCharAt(end, temp);
            start++;
            end--;
        }
    }
}
  • 时间复杂度: O(n)
  • 空间复杂度: O(n), java 中字符串不可变,因此一般涉及到字符串的最少也是 O(n)

剑指 Offer 58 - II. 左旋转字符串

字符串的左旋转操作是把字符串前面的若干个字符转移到字符串的尾部。请定义一个函数实现字符串左旋转操作的功能。比如,输入字符串"abcdefg"和数字2,该函数将返回左旋转两位得到的结果"cdefgab"。

image.png

思路

解法一: 本题其实就是将字符串前 n 个字符转移到字符串末尾。

解法二: 先整个字符串翻转,再反转前面的,最后反转后面n个

假设原字符串为:s = "abcdefg", k = 2

  • 首先翻转区间为前n的子串 s = “bacdefg”
  • 然后翻转从n到末尾的子串: s = "bagfedc"
  • 最后反转整个字符串:s = "cdefgab"

代码实现

切片(substring)-解法一

class Solution {
    public String reverseLeftWords(String s, int n) {
        return s.substring(n, s.length()) + s.substring(0,n);
    }
}
  • 时间复杂度: O(n)
  • 空间复杂度: O(n), 两个字符串切片的总长度为 n

如果不允许使用切片-解法二

class Solution {
    public String reverseLeftWords(String s, int n) {
        char[] ch = s.toCharArray();
        reverseString(ch, 0, n - 1);
        reverseString(ch, n, ch.length - 1);
        reverseString(ch, 0, ch.length - 1);
        return new String(ch);
    }

    public void reverseString(char[] ch, int left, int right) {
        while (left < right) {
            char temp = ch[left];
            ch[left] = ch[right];
            ch[right] = temp;
            left++;
            right--;
        }
    }
}
  • 时间复杂度: O(n)
  • 空间复杂度: O(n)