【字符串第一篇】题目:344. 反转字符数组 && leetcode206反转链表

166 阅读5分钟

文章目录

leetcode344,反转字符数组

问题描述

解决方案:设置一个暂存变量就好了

这个题目的意义:「如果题目关键的部分直接用库函数就可以解决,建议不要使用库函数。」
「如果库函数仅仅是 解题过程中的一小部分,并且你已经很清楚这个库函数的内部实现原理的话,可以考虑使用库函数。」

0^ 0=0; 0^ 1=1; 1^ 0=1; 1^ 1=0

class Solution {
    public void reverseString(char[] s) {
       for (int left=0,right=s.length-1; left<right; left++,right--){
            char mychar = s[left];
            s[left] = s[right];
            s[right] = mychar;
       }
    }
}

可以向下面一样,改成while循环,如下:

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

没有必要输出,只要程序执行结束,

时间复杂度:O(N),其中 NN 为字符数组的长度。一共执行了 N/2N/2 次的交换。
空间复杂度:O(1)。只使用了常数空间来存放若干变量。

题目:541. 反转字符串II

问题描述

给定一个字符串 s 和一个整数 k,你需要对从字符串开头算起的每隔 2k 个字符的前 k 个字符进行反转

如果剩余字符少于 k 个,则将剩余字符全部反转。

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

示例:
输入: s = “abcdefg”, k = 2
输出: “bacdfeg”

思路

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

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

「所以当需要固定规律一段一段去处理字符串的时候,要想想在在for循环的表达式上做做文章。」

解法

class Solution {
    public String reverseStr(String s, int k) {
      char[] charArray = s.toCharArray();
      for (int start=0;start<charArray.length-1;start = start + 2*k){
          int i=start ; int j =Math.min(start+k-1,charArray.length -1);
          while (i < j){
            char temp = charArray[i];
            charArray[i]=charArray[j]; i++;  
            charArray[j]=temp; j--;
          }
      }
      return new String(charArray);  // 用toString()会有空指针风险,用+""不优美
    }
}

这样写更容易看懂,就是交换元素之后,移动两个指针

class Solution {
    public String reverseStr(String s, int k) {
      char[] charArray = s.toCharArray();
      for (int start=0;start<charArray.length-1;start = start + 2*k){
          int i=start ; int j =Math.min(start+k-1,charArray.length -1);
          while (i < j){
            char temp = charArray[i];
            charArray[i]=charArray[j]; 
            charArray[j]=temp; 
            i++;
            j--;
          }
      }
      return new String(charArray);  // 用toString()会有空指针风险,用+""不优美
    }
}

可以向上面一样,写成一个for循环,如下:

class Solution {
    public String reverseStr(String s, int k) {
      char[] charArray = s.toCharArray();
      for (int start=0;start<charArray.length-1;start = start + 2*k){
          for(int i=start,j =Math.min(start+k-1,charArray.length -1);i < j;i++,j--){
            char temp = charArray[i];
            charArray[i]=charArray[j]; 
            charArray[j]=temp; 
          }
      }
      return new String(charArray);  // 用toString()会有空指针风险,用+""不优美
    }
}

int j =Math.min(start+k-1,charArray.length -1);
三种情况,如果剩余元素大于2k,j=start+k-1,
如果剩余元素大于k小于2k,j=start+k-1,
如果剩余元素小于k,j=a.length-1,此时,还是满足 i<j ,可以进入while循环

关于while(i<j)循环体

leetcode206,反转链表

问题描述

反转链表(头插法+改变指针指向)

class Solution {
    public ListNode reverseList(ListNode head) {
      ListNode first =  new ListNode(-1); // 新建一个虚拟头节点
      first.next=null;  // 并设置next为null,这里是为了使用代码对称,其实不设置这个也可以
      ListNode cur=head;  // p执行指向原链表实际头结点
      while(cur!=null){ 
         ListNode temp = cur.next;  // 因为下面要修改cur.next,所以暂存cur.next
         cur.next = first.next;
         first.next = cur;
         cur=temp;     // while循环中最后一句就是实现指针cur的移动
      }
      return first.next;  // first本身这个元素不要(就是val为-1的这个)
    }
}

关于返回值:这里最后的first.next是指向第一个实体头结点的。

class Solution {
    public ListNode reverseList(ListNode head) {
      ListNode cur=head;
      ListNode pre=null;
      while(cur!=null){
         ListNode temp = cur.next;  // 保存一下 cur的下一个节点,因为接下来要改变cur->next
         cur.next = pre;  // 这里就是翻转操作,pre里面存放着上一个元素(因为cur曾经指向上一个元素,又pre=cur),cur操作当前所指向的链表元素的next指针变为指向上一个元素,
          // 更新pre 和 cur指针
         pre = cur;
         cur = temp;  // while循环中最后一句就是实现指针cur的移动
      }
      return pre;
    }
}

关于返回值:最后的pre是指向最后一个元素的,而此时链表所有的next指针都反转过来了,所以pre指向的就是第一个实体头结点。

题目:151,反转字符串里的单词(有空格的就是单词,所以必须识别空格)

解法(调用底层静态方法)

class Solution {
    public String reverseWords(String s) {
      s=s.trim();  // 先去掉字符串首尾空格,就仅仅剩下中间的空格了,
      List list = Arrays.asList(s.split("\\s+"));   // 数组类的静态方法,参数为数组,返回值为list
      Collections.reverse(list);   // Collection类的静态方法,参数为list,返回值为list
      return String.join(" ",list);  // String类的静态方法,参数为list,返回为String
    }
}

小结:
先去掉字符串首尾空格,就仅仅剩下中间的空格了
然后按照中间的空格split,得到结果Arrays.asList变为一个List
然后对list中的每一个item元素进行反转
最后用String.join,将list中的各个元素使用空格连接起来

看题目,因为要反转的是整个句子中的各个单词,而不是每个单词中的各个字符,所以就放到list中

public String[] split(String regex)根据给定的正则表达式的匹配来拆分此字符串。正则表达式中,
\s表示 空格,回车,换行等空白符,
+号表示一个或多个的意思

split("\s+") 按空格,制表符,等进行拆分(也就是说它是按空白部分进行拆分,不管这个空白使用设么操作留下的,比如空格键、tab键)
split(" +") 按空格进行拆分(也就是说只有按空格键流出来的空白才会是拆分的一句)

String.join()方法是JDK1.8之后新增的一个静态方法,使用方式如下所示:
String result = String.join("-",“a”,“b”,“c”,“d”);
输出结果如下:a-b-c-d
也可使用如下方式:
String[] arr = {“a”,“b”,“c”,“d”};
String result = String.join("-",arr);
输出结果如下:a-b -c-d
参数列表:
1、表示连接的符号
2、表示被连接的数组(也可以是集合),或者是要连接的多个字符串

四个函数用自己的代码表述(实际上就是抄开源代码)

如下: