Day8
前言
希望从今天开始跟上进度
文章:代码随想录
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的翻转关于空格的处理,还不是很理解,晚上回宿舍理解一下争取明天补充完整