摘要
本文是日常算法学习记录。今天完成了344.反转字符串、541. 反转字符串II、卡码网:54.替换数字、151.翻转字符串里的单词、卡码网:55.右旋转字符串,共五题,并用文字详细描述了算法运行过程。
344、反转字符串
思路分析
这道题给了一个字符数组,要求反转整个数组,而且需要原地修改,使用O(1)
的额外空间。
幸好给的是字符数组,否则给字符串的话,Java就无法使用O(1)
的额外空间了。
思路很清晰很明确,那就是使用指针i
从数组头部开始,指针j
从数组尾部开始,同时向数组中间遍历,每次遍历互换i
、j
位置处的元素即可,直到i=j
。
代码实现
public void reverseString(char[] s) {
int i = 0, j = s.length - 1;
do {
char tmp = s[i];
s[i] = s[j];
s[j] = tmp;
} while (++i < --j);
}
541、反转字符串II
思路分析
这道题与上一题《344.反转字符串》形似,但在反转的规则上有所变化。
因此,先数m*(2k)
个字符,分别交换其前k
个字符。最后再单独对n-m*(2k)
个字符进行反转即可。
代码实现
public String reverseStr(String s, int k) {
char[] chars = s.toCharArray();
int i, p, q;
for (i = 0; chars.length - i >= k << 1; i += k << 1) {
p = i;
q = i + k - 1;
do {
char tmp = chars[p];
chars[p] = chars[q];
chars[q] = tmp;
} while (++p < --q);
}
p = i;
if (chars.length - i < k) {
q = chars.length - 1;
} else {
q = i + k - 1;
}
while (p < q) {
char tmp = chars[p];
chars[p++] = chars[q];
chars[q--] = tmp;
}
return new String(chars);
}
卡码网:54、替换数字
思路分析
卡码网题目不同于LeetCode题目之处在于其编码方式要使用ACM方式,故而需要处理输入输出,且要写出完整的.java
文件。
算法部分,要将数字全部替换为"number"
。对于Java来说,要操作字符串,则必须申请额外空间。因此可以直接实例化一个StringBuilder
类的对象sb
,用于构造新的字符串。随后对输入的字符串逐字符遍历读取,如果当前字符c
不是小写英文字母(其ASCII码
小于97
),就给sb
接上字符串"number"
,否则直接接上c
。
代码实现
import java.util.*;
public class Main{
public static void main(String[] args){
Scanner sc=new Scanner(System.in);
while(sc.hasNextLine()){
String s=sc.nextLine();
StringBuilder sb=new StringBuilder();
for(int i=0;i<s.length();i++){
char c=s.charAt(i);
sb.append(c<97?"number":c);
}
System.out.println(sb);
}
}
}
151、翻转字符串里的单词
思路分析
这是一道很经典的字符串操作算法题。
定义两个指针,i
、j
。i
表示当前读取的字符,j
表示当前单词的尾字符。
实例化一个StringBuilder
类对象sb
,用于构造翻转后的新字符串
让i
从字符串尾部从后往前遍历,如果i
处字符为空格的话,我们就认定i-1
的位置是下一个单词的位置,从而把j
设为i-1
,记录为下一个单词的结尾。
如果i
的下一个遍历之处,也就是i-1
处的字符为空格,那么我们就认定遍历到了当前单词的首字母,从而从i
处开始遍历到j处,将读取到的每一个字符放入到sb
中,并在最后加上一个空格。
由于新字符串首尾不允许有空格,因此如果新字符串最后一个字符为空格的话,还要删除。
代码实现
public String reverseWords(String s) {
StringBuilder sb = new StringBuilder();
for (int i = s.length() - 1, j = i; i >= 0; i--) {
if (s.charAt(i) == ' ') {
j = i - 1;
} else if (i == 0 || s.charAt(i - 1) == ' ') {
for (int k = i; k <= j; k++) {
sb.append(s.charAt(k));
}
if (i > 0) {
sb.append(' ');
}
}
}
if (sb.charAt(sb.length() - 1) == ' ') {
sb.deleteCharAt(sb.length() - 1);
}
return sb.toString();
卡码网:55.右旋转字符串
思路分析
先调试好输入输出。
这道题有多重算法选择。
方法一。对字符串进行切片操作,即前n-k
个字符的子串,和后k
个字符的子串,然后将前者拼接到后者后面。这种方法由于需要对字符串进行切片操作,所以时间复杂度为O(n)
,空间复杂度也为O(n)
。
方法二。将后k
个字符逐个向前移动,直到全部移动到开头。每次移动使用类似swap
的操作,每个字符移动到开头,时间复杂度为O(n)
,k
个字符需要O(kn)
。如果不算Java必须的字符串转字符数组的空间,其空间复杂度为O(1)
。
方法三。将整个字符串反转(reverse)
,在分别对前k
个字符和后n-k
个字符进行反转。三次反转,时间复杂度分别为O(n)
、O(k)
、O(n-k)
,所以整体为O(n)
。如果不算Java必须的字符串转字符数组的空间,其空间复杂度为O(1)
。
代码实现
展示对方法三的实现。
import java.util.*;
public class Main{
public static void main(String[] args){
Scanner sc=new Scanner(System.in);
while(sc.hasNextLine()){
int k=Integer.parseInt(sc.nextLine());
String s=sc.nextLine();
char[] arr=s.toCharArray();
for(int i=0,j=arr.length-1;i<j;i++,j--){
arr[i]^=arr[j];
arr[j]^=arr[i];
arr[i]^=arr[j];
}
for(int i=k,j=arr.length-1;i<j;i++,j--){
arr[i]^=arr[j];
arr[j]^=arr[i];
arr[i]^=arr[j];
}
for(int i=0,j=k-1;i<j;i++,j--){
arr[i]^=arr[j];
arr[j]^=arr[i];
arr[i]^=arr[j];
}
System.out.println(new String(arr));
}
}
}
总结和思考
以上就是今天的算法学习记录,我们通过分析思路和实现代码的方式,解决了这道题目。希望对大家的算法学习有所帮助,谢谢阅读!