代码随想录Day8 | 344. 反转字符串,541. 反转字符串II,卡码网54. 替换数字,151. 翻转字符串里的单词,卡码网55. 右旋转字符串

Day8

前言

希望从今天开始跟上进度

文章:代码随想录

视频:算法公开课-跟着Carl学算法

LeetCode 344 反转字符串

自己思路

所谓反转字符串,其实就是将字符数组的第一位和最后一位交换,第二位和倒数第二位交换,依次递推。最先写的时候,还想用for循环,先根据字符数组长度求出总交换次数,这个方法可行但是略复杂,其实直接用while循环i<j就可以,感觉比较简单

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

看完讲解

双指针,代码同上

LeetCode 541 反转字符串II

自己思路

感觉我的思路完全是顺逻辑,所以写起来可能比较复杂,题目的意思如果简单的来讲,其实就是第一段k个反转,第二段k个不反转,以此类推,然后最后剩下的不足k个全部反转(当然这个必须是跟在不反转的后一段的)

先进行字符串转数组,然后先用长度除以2k就知道会有几段反转,并用remain存下剩余的个数。如果剩余的个数比k大,那么count加1,这样就求出了总反转次数,然后先处理这个。使用两个指针,两个指针指向该k段的头和尾,然后对该段进行反转(上一题的方法),完成一次反转后这两个指针都前进2k

最后就是对于remain少于k的处理,需要全部反转。因为离开上面的循环时,一个指针已经指向最后一段的头,所以只需要把另一个指针指向最后一位,然后再次进行段内反转操作即可,最后再数组转字符串返回

class Solution {
    public String reverseStr(String s, int k) {
        char[] arr = s.toCharArray();

        int count = arr.length / (2 * k);
        int remain = arr.length % (2 * k);

        if ((remain >= k) && (remain < (2 * k))) {
            count++;
        }

        int i = 0;
        int j = i + k - 1;
        while (count != 0) {
            int left = i;
            int right = j;
            while (left < right) {
                char temp = arr[left];
                arr[left] = arr[right];
                arr[right] = temp;
                left++;
                right--;
            }
            i = i + (2 * k);
            j = j + (2 * k);
            count--;
        }

        if((remain > 0) && (remain < k)){
            j = arr.length - 1;
            while (i < j) {
                char temp = arr[i];
                arr[i] = arr[j];
                arr[j] = temp;
                i++;
                j--;
            }
        }

        return new String(arr);
    }
}

看完讲解

看完视频确实还是双指针,反转的算法不难基本和上面一题一致,难的是对逻辑的处理,先统计反转次数,再while循环确实太复杂了,没想到这里直接for循环,一次前进2k步,以超出长度为限制其实可以让代码简洁很多

另外就是我前面也有想到把反转逻辑单拎出来作为函数,但是当时觉得还要把需要反转的部分单独作为一个数组传入好复杂,其实这里有误区:可以直接传入整个字符串数组,反转起点,反转终点,三个参数就可以

这里有一个对剩下的到底是多少个元素的巧妙处理,因为多于k个处理前k个(此时i+k-1不会越界),少于k个就全部反转(此时i+k-1越界),所以直接用min函数在i+k-1和数组最后一个index取最小值即可,代码变得简洁很多

容易出错的还有2k要写成2*k,不然会报错

class Solution {
    public String reverseStr(String s, int k) {
        char[] arr = s.toCharArray();
        for (int i = 0; i < arr.length; i = i + 2 * k) {
            int left = i;
            int right = Math.min((i + k - 1), (arr.length - 1));
            while (left < right) {
                char temp = arr[left];
                arr[left] = arr[right];
                arr[right] = temp;
                left++;
                right--;
            }
        }
        return new String(arr);
    }
}

卡码网 54 替换数字

自己思路

我的思路就是依次遍历字符数组,然后用StringBuilder容器来接收,如果是数字,就append number,如果是字母,就直接append。但是不知道直接用StringBuilder可不可以,而且第一次写卡码网的代码,ACM模式还不太熟练,需要写上main函数以及导入需要的包,感觉写的没问题,但是报错Main.java:3: error: class Test is public, should be declared in a file named Test.java,不知道怎么解决

// 报错:Main.java:3: error: class Test is public, should be declared in a file named Test.java

import java.util.Scanner;

public class Test{
    public static void main (String[] args) {
        Scanner sc = new Scanner(System.in);
        String s = sc.next();
        char[] arr = s.toCharArray();
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < arr.length; i++) {
            if (arr[i] >= '0' && arr[i] <= '9') {
                sb.append("number");
            }else {
                sb.append(arr[i]);
            }
        }
        System.out.println(sb.toString());
    }
}

看完讲解

首先明白了卡码网的代码要求,在这里类名必须是Main,改成这个之后,前面的报错就解决了。总结一下卡码网的要求:①需要用到的包需要import;②类名需要是Main;③需要写main函数入口

import java.util.Scanner;

public class Main{
    public static void main (String[] args) {
        Scanner sc = new Scanner(System.in);
        String s = sc.next();
        char[] arr = s.toCharArray();
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < arr.length; i++) {
            if (arr[i] >= '0' && arr[i] <= '9') {
                sb.append("number");
            }else {
                sb.append(arr[i]);
            }
        }
        System.out.println(sb.toString());
    }
}

当然这道题确实不是让用StringBuilder来解决的,还是用双指针,处理分为两步,一步数组扩容,一步进行数组处理,这里的关键在于,是从最后一位向前处理,这样可以避免需要再定义一个数组,以及从前到后会存在覆盖或者需要后移的问题

好的知道上面这个思维就好了,发现答案的代码,就是用的StringBuilder,但是不需要把字符串转成数组了,可以直接就对字符串进行处理,如下:

import java.util.Scanner;

public class Main{
    public static void main (String[] args) {
        Scanner sc = new Scanner(System.in);
        String s = sc.next();
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < s.length(); i++) {
            if (s.charAt(i) >= '0' && s.charAt(i) <= '9') {
                sb.append("number");
            }else {
                sb.append(s.charAt(i));
            }
        }
        System.out.println(sb.toString());
    }
}

LeetCode 151 翻转字符串里的单词

自己思路

累了,感觉写了好半天都还是有错。说一下我的思路:字符串从后向前遍历,定义temp来存储单独的单词,但是因为从后向前,所以temp应该会反。然后如果遇到空格就在result后加上反转后的temp,并及时把temp置空。但是问题来了,我发现如果在遇到空格给result赋值时再加上单词间空格的话会出现两个单词间多个空格,如果在反转函数中加空格,就会给最后一个单词后多了空格,难搞。这里还出现了另一个问题是,我竟然在反转函数中直接去操作字符串,而不是先变成数组,忘记了字符串是不可变的

看完讲解

看完视频的确视频的思路更好,整体反转+局部反转,分为三步,第一步去掉多余的空格,第二步反转整个字符串,最后一步反转逐个单词。关于反转的操作就不再赘述,主要是如何去掉多余的空格,这个很像之前的数组移除元素,采用双指针,快指针遍历整个字符串数组,慢指针指向当前填入的位置,但是关于这个里面需要留存空格的处理(开头不留存),确实有点难理解,看视频看了五六次还是不懂,第二天自己直接在草稿纸上人工debug,顺着代码把每一步画出来,终于明白了,感觉要理解成先跳过(删掉)所有的空格+第二个单词起前补一个空格的模式,即先删光再补,会好懂很多

代码的详细逻辑就不再赘述了,而且这次把注释补的很完整,还是先尽量自己写的代码。注意第22行和第51行的while循环,判断条件是是一定要加上fast<arr.length的,否则如果fast指向最后一位后,进入循环fast++,等再次进入while判断的时候fast超出index范围会空指针异常。然后反转函数还是写成三个参数那种,这样整体反正和局部反转就都可以用,局部反转就变成了找反转起点和终点的问题。最后写完还是有问题,找不到错误,对比答案发现是在第一步去掉多余的空格后,还需要重置数组的大小,大小刚好就是slow,所以要重新new一个数组,进行arraycopy之后再返回,很经典且有难度的一题

class Solution {
    public String reverseWords(String s) {
        char[] arr = s.toCharArray();
        // 1.移除多余空格(只保留单词之间的一个空格)
        arr = removeSpace(arr);
        // 2.整体反转
        reverseArr(arr, 0, arr.length-1);
        // 3.单词反转
        reverseWord(arr);
        return new String(arr);
    }

    // 1.移除多余空格(只保留单词之间的一个空格)
    public static char[] removeSpace(char[] arr) {
        int slow = 0;
        for (int fast = 0; fast < arr.length; fast++) {
            if (arr[fast] != ' ') {// 用来跳过所有的空格
                if (slow != 0) {// 遇到单词的第一刻前补空格(开头不补),后面会进入while所以只会补一次
                    arr[slow] = ' ';
                    slow++;
                }
                while ((fast < arr.length) && (arr[fast] != ' ')) {
                    arr[slow] = arr[fast];
                    slow++;
                    fast++;
                }
            }
        }
        char[] newArr = new char[slow];
        System.arraycopy(arr, 0, newArr, 0, slow);
        return newArr;
    }

    // 2.整体反转
    public static void reverseArr(char[] arr, int left, int right) {
        while (left < right) {
            char temp = arr[left];
            arr[left] = arr[right];
            arr[right] = temp;
            left++;
            right--;
        }
    }

    // 3.单词反转
    public static void reverseWord(char[] arr) {
        // 确认每个单词的left和right的过程
        for (int fast = 0; fast < arr.length; fast++) {
            // 单词起点一致
            int slow = fast;
            while ((fast < arr.length) && (arr[fast] != ' ')) {
                fast++;
            }
            reverseArr(arr, slow, fast-1);
        }
    }
}

卡码网 55 右旋转字符串

自己思路

有点累了,感觉我如果想出来的思路,一定会太复杂了,直接去看讲解的思路了

看完讲解

思路真的很妙整体反转+局部反转,和上一题一样,把整个字符串分为两段之后,采用先整体反转,再段内反转的操作,所以以后如果遇到类似的问题,可以优先考虑先整体反转+再局部反转的思路,至于具体反转的代码就是老生常谈双指针,不再赘述

写代码主要就注意两点,一是反转函数是可以没有返回值的,因为操作的是引用数据类型的数组,然后就是可以把反转函数写三个参数,包含数组对象,反转起点和反转终点,这样子不论是对谁反转都可以使用同一个函数了

import java.util.Scanner;

public class Main{
    public static void main (String[] args) {
        Scanner sc = new Scanner(System.in);
        int k = sc.nextInt();
        String s = sc.next();
        char[] arr = s.toCharArray();
        
        reverse(arr, 0, arr.length-1);
        reverse(arr, 0, k - 1);
        reverse(arr, k, arr.length-1);
        
        System.out.println(new String(arr));
    }
    
    public static void reverse(char[] arr, int left, int right) {
        while (left < right) {
            char temp = arr[left];
            arr[left] = arr[right];
            arr[right] = temp;
            left++;
            right--;
        }
    }
}

总结

用时:6h,感觉自从写算法以来,每天都很累当然也很充实

151的翻转关于空格的处理,还不是很理解,晚上回宿舍理解一下争取明天补充完整

  • 22
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值