代码随想录算法训练营第八天 | 344.反转字符串 ● 541. 反转字符串II ● 剑指Offer 05.替换空格 ● 151.翻转字符串里的单词 ● 剑指Offer58-II.左旋转字符串

今日任务

344.反转字符串

541. 反转字符串II

剑指Offer 05.替换空格

151.翻转字符串里的单词

剑指Offer58-II.左旋转字符串

详细布置

344.反转字符串

建议: 本题是字符串基础题目,就是考察 reverse 函数的实现,同时也明确一下 平时刷题什么时候用 库函数,什么时候 不用库函数

题目链接/文章讲解/视频讲解:https://programmercarl.com/0344.%E5%8F%8D%E8%BD%AC%E5%AD%97%E7%AC%A6%E4%B8%B2.html

思路:临时变量temp,遍历到长度一半,交换s[i] 与 s[s.length - i -1]。

class Solution {
    public void reverseString(char[] s) {
        char temp = ' ';
        for (int i = 0; i < s.length / 2; i++) {
            temp = s[i];
            s[i] = s[s.length - 1 - i];
            s[s.length - 1 - i] = temp;
        }
    }
}

2)双指针

class Solution {
    public void reverseString(char[] s) {
        int l = 0;
        int r = s.length - 1;
        while (l < r) {
            s[l] ^= s[r];  //构造 a ^ b 的结果,并放在 a 中
            s[r] ^= s[l];  //将 a ^ b 这一结果再 ^ b ,存入b中,此时 b = a, a = a ^ b
            s[l] ^= s[r];  //a ^ b 的结果再 ^ a ,存入 a 中,此时 b = a, a = b 完成交换
            l++;
            r--;
        }
    }
}

学习了用异或运算(^)来交换元素

541. 反转字符串II

建议:本题又进阶了,自己先去独立做一做,然后在看题解,对代码技巧会有很深的体会。

题目链接/文章讲解/视频讲解:https://programmercarl.com/0541.%E5%8F%8D%E8%BD%AC%E5%AD%97%E7%AC%A6%E4%B8%B2II.html

//题目的意思其实概括为 每隔2k个反转前k个,尾数不够k个时候全部反转
class Solution {
    public String reverseStr(String s, int k) {
        char[] ch = s.toCharArray();
        for(int i = 0; i < ch.length; i += 2 * k){
            int start = i;
            //这里是判断尾数够不够k个来取决end指针的位置
            int end = Math.min(ch.length - 1, start + k - 1);
            //用异或运算反转 
            while(start < end){
                ch[start] ^= ch[end];
                ch[end] ^= ch[start];
                ch[start] ^= ch[end];
                start++;
                end--;
            }
        }
        return new String(ch);
    }
}

解法2

class Solution {
    public String reverseStr(String s, int k) {
        char[] ch = s.toCharArray();
        // 1. 每隔 2k 个字符的前 k 个字符进行反转
        for (int i = 0; i< ch.length; i += 2 * k) {
            // 2. 剩余字符小于 2k 但大于或等于 k 个,则反转前 k 个字符
            if (i + k <= ch.length) {
                reverse(ch, i, i + k -1);
                continue;
            }
            // 3. 剩余字符少于 k 个,则将剩余字符全部反转
            reverse(ch, i, ch.length - 1);
        }
        return  new String(ch);

    }
    // 定义翻转函数
    public void reverse(char[] ch, int i, int j) {
    for (; i < j; i++, j--) {
        char temp  = ch[i];
        ch[i] = ch[j];
        ch[j] = temp;
    }

    }
}

剑指Offer 05.替换空格

建议:对于线性数据结构,填充或者删除,后序处理会高效的多。好好体会一下。

题目链接/文章讲解:https://programmercarl.com/%E5%89%91%E6%8C%87Offer05.%E6%9B%BF%E6%8D%A2%E7%A9%BA%E6%A0%BC.html

方法一:

//使用一个新的对象,复制 str,复制的过程对其判断,是空格则替换,否则直接复制,类似于数组复制

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

总结:

学习StringBuilder的用法:

String类: 字符串的底层是一个被final修饰的数组,不能改变,是一个常量
StringBuilder类字符串缓冲区,底层是一个没有被final修饰的数组,可改变长度,可提高字符串的操作效率

构造方法:
	public stringBuilder():构造一个空的stringBuilder容器
	public stringBuilder(String str):构造一个StringBuilder容器,并将str添加进去

成员方法:
	public StringBuilder append(Object obj):添加任意类的字符串形式,并返回当前对象
		eg.
			StringBuilder sb1 = new StringBuilder();
			sb1.append(1);
			StringBuilder sb2 = sb1.append("abc");//sb2 == sb1
			//由于返回值为当前对象,所以可以进行链式编程
			sb1.append(1).append("abc").append(true);
	public String toString():将当前StringBuilder对象转换为String对象
	public StringBuilder reverse():反转内容,将容器中的每一个字母反转,eg."abcd"-->"dcba"

方法二:

双指针

 

class Solution {
    public String replaceSpace(String s) {
        StringBuilder sb = new StringBuilder();
				//扩充空间,有一个空格,长度 + 2
        for (int i = 0; i < s.length(); i++) {
            if (s.charAt(i) == ' '){
                sb.append("  ");
            }
        }
				//若是没有空格直接返回
        if (sb.length() == 0){
            return s;
        }
      	//有空格情况 定义两个指针
        int l = s.length() - 1;//左指针:指向原始字符串最后一个位置
        s += sb.toString();
        int r = s.length() - 1;//右指针:指向扩展字符串的最后一个位置
        char[] chars = s.toCharArray();
        while (l >= 0){
            if (chars[l] == ' '){
                chars[r--] = '0';
                chars[r--] = '2';
                chars[r] = '%';
            }else {
                chars[r] = chars[l];
            }
            l--;
            r--;
        }
        return new String(chars);
    }
}

151.翻转字符串里的单词

建议:这道题目基本把 刚刚做过的字符串操作 都覆盖了,不过就算知道解题思路,本题代码并不容易写,要多练一练。

题目链接/文章讲解/视频讲解:https://programmercarl.com/0151.%E7%BF%BB%E8%BD%AC%E5%AD%97%E7%AC%A6%E4%B8%B2%E9%87%8C%E7%9A%84%E5%8D%95%E8%AF%8D.html

 

算法思路:

从后向前,不停的把单词添加到 StringBuilder 中,最终返回结果

关键点:

如何修剪掉两端空格;

如何把单词反转过来;

如何跳过中间连续的空格。

class Solution {
    public String reverseWords(String s) {
        char[] charArray=s.toCharArray();
        int left=0,right=s.length()-1;
        // 清除字符串两边的空格
        // 清除左边
        while(charArray[left]==' '){
            left++;
        }
        // 清除右边
        while(charArray[right]==' '){
            right--;
        }
        StringBuilder sb=new StringBuilder();
        // 开始添加单词
        while(left<=right){
            int index=right;
            // index 向左遍历找到第一个空格
            while(index>=left&&charArray[index]!=' '){
                index--;
            }
            // 现在 index 已经找到第一个空格,i=index+1 后移到字符串出现的位置
            // 添加字符串
            for(int i=index+1;i<=right;i++){
                sb.append(charArray[i]);
            }
            // 如果不是最后一个单词,就添加空格
            if(index>left) sb.append(' ');

            // 使用 index 指针 跳过中间可能出现的空格
            while(index>=left&&charArray[index]==' '){
                index--;
            }
            // 把 right 放到下一个单词出现的位置,继续循环
            right=index;
        }
        return sb.toString();

    }
}

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

 

剑指Offer58-II.左旋转字符串

题目链接/文章讲解:https://programmercarl.com/%E5%89%91%E6%8C%87Offer58-II.%E5%B7%A6%E6%97%8B%E8%BD%AC%E5%AD%97%E7%AC%A6%E4%B8%B2.html

 

class Solution {
    public String reverseLeftWords(String s, int n) {
        String s1 = reverse(s);
        String s2 = s1.substring(0, s.length() - n);
        String s3 = s1.substring(s.length() - n, s.length());
        return reverse(s2) + reverse(s3);
    }
    public String reverse(String str){
        char[] chars = str.toCharArray();
        int l = 0, r = chars.length - 1;
        while (l < r){
            chars[l] ^= chars[r];
            chars[r] ^= chars[l];
            chars[l] ^= chars[r];
            l++;
            r--;
        }
        return new String(chars);
    }
}
解答成功:
		执行耗时:3 ms,击败了43.92% 的Java用户
		内存消耗:41.7 MB,击败了15.16% 的Java用户

解2:

class Solution {
    public String reverseLeftWords(String s, int n) {
        int len = s.length();
        if (n <= 0 || n >= len){
            return s;
        }
        StringBuilder sb = new StringBuilder(s);
        reverseString(sb, 0, len - 1);
        reverseString(sb, 0, len - n - 1);
        reverseString(sb, len - n, len - 1);
        return sb.toString();
    }

    public 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--;
        }
    }
}
解答成功:
		执行耗时:3 ms,击败了43.92% 的Java用户
		内存消耗:41.7 MB,击败了15.16% 的Java用户

总结

在这篇文章344.反转字符串(opens new window),第一次讲到反转一个字符串应该怎么做,使用了双指针法。

然后发现541. 反转字符串II(opens new window),这里开始给反转加上了一些条件,当需要固定规律一段一段去处理字符串的时候,要想想在在for循环的表达式上做做文章。

后来在151.翻转字符串里的单词(opens new window)中,要对一句话里的单词顺序进行反转,发现先整体反转再局部反转 是一个很妙的思路。

最后再讲到本题,本题则是先局部反转再 整体反转,与151.翻转字符串里的单词(opens new window)类似。

posted @ 2022-11-25 11:06  染醉霜林  阅读(20)  评论(0)    收藏  举报