参考资料:
剑指offer题目汇总
字符串(共9道题目): 模板:
题目描述:
解题思路 :
注意问题 :
补充知识点 :
2、替换空格 (08.31)
27、字符串的排列 (09.01)
34、第一个只出现一次的字符 (09.02)
43、左旋转字符串 (09.03)
44、反转单词序列 (09.04)
49、把字符串转换成整数 (09.05)(多种情况讨论,重点在于对目标的分析和归纳)
52、正则表达式匹配 (09.06)(多种情况讨论,重点在于对目标的分析和归纳)
53、表示数值的字符串 (09.07)(多种情况讨论,重点在于对目标的分析和归纳)
54、字符流中第一个不重复的字符 (09.08)
2、替换空格
题目描述: 请实现一个函数,将一个字符串中的每个空格替换成“%20” 。例如,当字符串为We Are Happy.则经过替换之后的字符串为We%20Are%20Happy。
解题思路 :把从前往后一次遍历处理的方法(时间复杂度:O(n^2))转化为两次遍历处理(第一次:统计空格数量,确定替换之后的总长度,第二次:从后往前设立两个指针处理,每次时间复杂度均为:O(n));
注意问题 :
1.for (i=0;i<2*j;i++) str.append(" ");
要先把不足的空间用append方法开辟出来,否则会报错。具体原因请参考StringBuffer类的charAt函数的写法,其中变量count
的定义为:public synchronized int length() {return count;}
,即length方法的返回值;
1 2 3 4 5 public synchronized char charAt (int index) { if ((index < 0 ) || (index >= count)) throw new StringIndexOutOfBoundsException(index); return value[index]; }
2.比较当前位置的字符是否是空格的时候,不能用str.charAt(k)==" "
。因为charAt方法的返回值是char类型,而" "
是字符串类型。Java的字符类型必须是单引号,字符串类型必须是严格的双引号(与python不同之处);所以可以采取使用单引号(str.charAt(k)==' '
)和定义一个字符类型变量(char c=' '
)两种方法;
2.题目中要求的返回类型是String,所以需要调用toString方法,把StringBuffer类数据类型转化为String类型(String类型也是类类型的数据类型);
补充知识点 :java字符串
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 public class Solution { public String replaceSpace (StringBuffer str) { int len=str.length(); int i; int j=0 ; for (i=0 ;i<len;i++) if (str.charAt(i)==' ' ) j++; int k=len-1 ; int l=len+2 *j-1 ; for (i=0 ;i<2 *j;i++) str.append(" " ); while (k>=0 ) { if (str.charAt(k)==' ' ) { k--; str.replace(l-2 ,l+1 ,"%20" ); l=l-3 ; } else { str.setCharAt(l,str.charAt(k)); k--; l--; } } return str.toString(); } }
27、字符串的排列
题目描述:
输入一个字符串,按字典序打印出该字符串中字符的所有排列。例如输入字符串abc,则打印出由字符a,b,c所能排列出来的所有字符串abc,acb,bac,bca,cab和cba。
解题思路 :由于字符长度不确定,所以不能通过设置多重循环来穷举。这是典型的递归例题。
——字符串(共9道题目)/1.png)
注意问题 :
1.加入链表之前需要判断是否有重复元素if (!res.contains(String.valueOf(a)))
,因为测试用例里面有重复字符,如:"aa"
;
2.全部加入链表之后要对链表中的字符串按字典序排序;关于java的排序方法详见:java的排序方法 ;
3.字符数组转化为字符串:String s=String.valueOf(a)
/ String s=new String(a)
;字符串转化为字符数组:char []a=str.toCharArray();
;
补充知识点 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 import java.lang.reflect.Array;import java.util.ArrayList;import java.util.Arrays;import java.util.Collections;public class Solution { ArrayList <String> res =new ArrayList<String>(); public ArrayList<String> Permutation (String str) { if (str==null || str.length()==0 ) return res; char []a=str.toCharArray(); Arrays.sort(a); Permutation(a,0 ); Collections.sort(res); return res; } public void Permutation (char []a,int begin) { int i; if (begin==a.length-1 ) { if (!res.contains(String.valueOf(a))) res.add(String.valueOf(a)); } else { for (i=begin;i<a.length;i++) { swap(a,i,begin); Permutation(a,begin+1 ); swap(a,i,begin); } } } public void swap (char []a,int i,int begin) { char t; t=a[i]; a[i]=a[begin]; a[begin]=t; } }
34、第一个只出现一次的字符
题目描述:
在一个字符串(0<=字符串长度<=10000,全部由字母组成)中找到第一个只出现一次的字符,并返回它的位置, 如果没有则返回 -1(需要区分大小写)。
解题思路 :桶排序;
注意问题 :
1.A-Z对应的ASCII码为65-90,a-z对应的ASCII码值为97-122;
2.java 获取字符串指定下标位置的值的方法:public char charAt(int index)
;
3.关于字符类型和ASCII表(java实际上是Unicode表)的使用:(int)'a'
:观察字符 ‘a’ 的ASCII值(在表中的位置),(char)97
:在表97的位置对应的字符。但本题中a[str.charAt(i)-'A']++;
也可以(我认为应该是:a[(int)str.charAt(i)-'A']++;
,感觉可以理解为:'a'-'A'
和97-65
的结果都是:32,如果把'a'-'A'
换成char c='a'; c-'A'
结果是等价的);
4.最后统计数组a中为1的项时,是依次拿这字符串中的每一个字符,判断其在数组中对应位置的值是否为1,这样可以保证如果值为1,则必定是第一个只出现一次的字符。如果遍历数组,找到第一个为1的项目,再去找其对应的字符和该字符对应的位置,则不能保证必定是第一个只出现一次的字符。此处是个易错点。
补充知识点 :java字符类型和Unicode表的转换、字母对应的Unicode值;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public class Solution { public int FirstNotRepeatingChar (String str) { if (str==null || str.length()==0 ) return -1 ; int []a=new int [122 -65 +1 ]; int i; int t=-1 ; for (i=0 ;i<str.length();i++) a[str.charAt(i)-'A' ]++; for (i=0 ;i<str.length();i++) if (a[str.charAt(i)-'A' ]==1 ) return i; return -1 ; } }
43、左旋转字符串
题目描述:
汇编语言中有一种移位指令叫做循环左移(ROL),现在有个简单的任务,就是用字符串模拟这个指令的运算结果。对于一个给定的字符序列S,请你把其循环左移K位后的序列输出。例如,字符序列S=”abcXYZdef”,要求输出循环左移3位后的结果,即“XYZdefabc”。是不是很简单?OK,搞定它!
解题思路 :三次反转算法(妙哉!)
注意问题 :
1.在对c[n,str.length()-1]进行反转时,极易出错;
2.数组反转方法:for(i=begin;i<=(end-begin)/2;i++)(i可以取到end);
3.注意数组的length是属性后面没有括号,字符串的length()是方法,后面有括号;
4.注意对字符串为空和长度为零的判断和对n为0和str.length()两种极端情况的判断。因为这四个判断,多提交了三次;
补充知识点 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 public class Solution { public String LeftRotateString (String str,int n) { if (str==null || str.length()==0 ) return "" ; if (n==0 || n==str.length()) return str; int i; char []c=str.toCharArray(); char t; for (i=0 ;i<=(n-1 )/2 ;i++) { t=c[i]; c[i]=c[n-1 -i]; c[n-1 -i]=t; } for (i=n;i<=(str.length()-1 -n)/2 +n;i++) { t=c[i]; c[i]=c[str.length()-1 -i+n]; c[str.length()-1 -i+n]=t; } for (i=0 ;i<=(str.length()-1 )/2 ;i++) { t=c[i]; c[i]=c[str.length()-1 -i]; c[str.length()-1 -i]=t; } return String.valueOf(c); } }
44、反转单词序列
题目描述:
牛客最近来了一个新员工Fish,每天早晨总是会拿着一本英文杂志,写些句子在本子上。同事Cat对Fish写的内容颇感兴趣,有一天他向Fish借来翻看,但却读不懂它的意思。例如,“student. a am I”。后来才意识到,这家伙原来把句子单词的顺序翻转了,正确的句子应该是“I am a student.”。Cat对一一的翻转这些单词顺序可不在行,你能帮助他么?
解题思路 :
第一步:反转整个序列中所有的字符,这时会发现不但反转了单词的顺序,单词中的字母顺序也被反转,因此需要第二步的调整。
第二步:以空格为分隔,依次反转每个单词,即让每个单词会到原来的正常顺序。
注意问题 :
1.反转函数:数列思想;
1 2 3 4 5 6 for (i=begin;i<=(end-begin)/2 +begin;i++){ t=array[i]; array[i]=array[end+begin-i]; array[end+begin-i]=t; }
2.String str=null与String str=“”的区别:String str1= null; str1引用为空,String str2= “”; str2引用一个空字符串。即:””分配了内存;null没有分配内存,因此str1还不是一个实例化的对象,而str2已经实例化;
3.注意考虑特殊情况:输入的字符串只包含多了空格:if (strArr[i]==' ' && strArr[i+1]!=' ')
;
补充知识点 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 public class Solution { public String ReverseSentence (String str) { if (str==null ) return null ; if (str.length()==0 ) return "" ; int i; int j; char []strArr=str.toCharArray(); char t; for (i=0 ;i<=(str.length()-1 )/2 ;i++) { t=strArr[i]; strArr[i]=strArr[str.length()-1 -i]; strArr[str.length()-1 -i]=t; } int begin=0 ; for (i=0 ;i<str.length()-1 ;i++) { if (strArr[i]==' ' && strArr[i+1 ]!=' ' ) { for (j=begin;j<=(i-1 -begin)/2 +begin;j++) { t=strArr[j]; strArr[j]=strArr[i-1 -j+begin]; strArr[i-1 -j+begin]=t; } begin=i+1 ; } } for (j=begin;j<=(str.length()-1 -begin)/2 +begin;j++) { t=strArr[j]; strArr[j]=strArr[str.length()-1 +begin-j]; strArr[str.length()-1 +begin-j]=t; } return String.valueOf(strArr); } }
49、把字符串转换成整数
题目描述:
将一个字符串转换成一个整数(实现Integer.valueOf(string)的功能,但是string不符合数字要求时返回0),要求不能使用字符串转换整数的库函数。 数值为0或者字符串不是一个合法的数值则返回0。
解题思路 :第一位情况较多,所以第一位要分多种情况讨论(+、-、数字、其他),第2—str.length()-1位只有两种情况(是数字,不是数字),所以先讨论第2—str.length()-1位,再讨论第一位。当第一位为+、-、数字,后面全为数字时,他们的核心问题是一样的:把一串数字字符串转化为整数。而解决该问题的关键在于找到第一位非零数字。
注意问题 :
1.if (str==null || str.length()==0)
,该问题已强调多次;
2.public String substring(int startpoint)
获得当前字符串的子串,该字串是从当前字符串的startpoint处截取到最后所得到的字符串;
3.找第一位非零数字时,用了设标志位的方法,比较巧妙,属于自创;
4.(int)str.charAt(i)-48;
数字字符转数字,因为 ‘0’ 对应的ASCII值是48;’
补充知识点 :String类的常用方法
备注 :通过这道题我就感觉,没什么问题是我想不明白的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 public class Solution { public int StrToInt (String str) { int i; int sum; if (str==null || str.length()==0 ) return 0 ; for (i=1 ;i<str.length();i++) if (str.charAt(i)<'0' || str.charAt(i)>'9' ) return 0 ; if (str.charAt(0 )=='+' ) return fun(str.substring(1 )); if (str.charAt(0 )=='-' ) return -1 *fun(str.substring(1 )); if (str.charAt(0 )>='0' && str.charAt(0 )<='9' ) return fun(str); return 0 ; } public int fun (String str) { int i=0 ; int len=str.length(); int sum=0 ; int f=0 ; for (i=0 ;i<len;i++) { if (str.charAt(i)!='0' ) f=1 ; if (f==1 ) { sum*=10 ; sum+=(int )str.charAt(i)-48 ; } } return sum; } }
52、正则表达式匹配
题目描述:
请实现一个函数用来匹配包括’.
‘和’*
‘的正则表达式。模式中的字符’.
‘表示任意一个字符,而’*
‘表示它前面的字符可以出现任意次(包含0次)。 在本题中,匹配是指字符串的所有字符匹配整个模式。例如,字符串”aaa”与模式”a.a
“和”ab*ac*a
“匹配,但是与”aa.a
“和”ab*a
“均不匹配。
解题思路 :核心在于分类和各种情况的讨论。对于分类找到找到了一条清晰的主线:同时结束,都未结束,一个结束;对于都未结束的情况又需要分成多种情况(3种以上),这种分法虽然大体上清晰,但情况太多,容易漏掉,建议采用参考资料上的方法。(标准代码:把所有false的情况列出来)
注意问题 :
1.涉及到i+1或j+1时,必须先判断是否超出界限;
2.最后一个return false;
确实起作用,具体见代码注释;
补充知识点 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 public class Solution { public boolean match (char [] str, char [] pattern) { if (str==null || pattern==null ) return false ; return match(str,0 ,pattern,0 ); } public boolean match (char [] str,int i,char [] pattern,int j) { if (i==str.length && j==pattern.length) return true ; if (i<str.length && j==pattern.length) return false ; if (i==str.length && j<pattern.length) if (j+1 == pattern.length-1 && pattern[j+1 ]=='*' ) return true ; else return false ; if (i<str.length && j<pattern.length) { if (i==str.length-1 && j==pattern.length-1 ) if (str[i]==pattern[j] || pattern[j]=='.' ) return match(str,i+1 ,pattern,j+1 ); if (i<str.length && j+1 <pattern.length) { if (pattern[j+1 ]=='*' ){ if (str[i]==pattern[j] || pattern[j]=='.' ) return match(str,i,pattern,j+2 ) || match(str,i+1 ,pattern,j+2 ) || match(str,i+1 ,pattern,j); else return match(str,i,pattern,j+2 ); } else { if (str[i]==pattern[j] || pattern[j]=='.' ) return match(str,i+1 ,pattern,j+1 ); else return false ; } } } return false ; } }
53、表示数值的字符串
题目描述:
请实现一个函数用来判断字符串是否表示数值(包括整数和小数)。例如,字符串”+100”, “5e2”, “-123”,” 3.1416” 和 “-1E-16” 都表示数值。 但是 “12e”, “1a3.14”, “1.2.3”, “+-5” 和 “12e+4.3” 都不是。
解题思路 :主要还是对各种情况的分类讨论。把所有的false的情况都单列出来,最后留下的情况就是true。所以最后一句是return true
;对数字特征的分析和归纳是关键。
注意问题 :
补充知识点 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 public class Solution { public boolean isNumeric (char [] str) { if (str==null ) return false ; boolean sign=false ,decimal=false ,hasE=false ; for (int i=0 ;i<str.length;i++){ if (str[i]=='e' ||str[i]=='E' ){ if (i==str.length-1 ) return false ; if (hasE) return false ; hasE=true ; }else if (str[i]=='.' ){ if (hasE||decimal) return false ; decimal=true ; }else if (str[i]=='+' ||str[i]=='-' ){ if (!sign && i!=0 && str[i-1 ]!='e' && str[i-1 ]!='E' ) return false ; if (sign && str[i-1 ]!='E' && str[i-1 ]!='e' ) return false ; sign=true ; }else if (str[i]>'9' ||str[i]<'0' ) return false ; } return true ; } public boolean isNumeric (char [] str) { String string = String.valueOf(str); return string.matches("[\\+-]?[0-9]*(\\.[0-9]*)?([eE][\\+-]?[0-9]+)?" ); } }
54、字符流中第一个不重复的字符
题目描述:
请实现一个函数用来找出字符流中第一个只出现一次的字符。例如,当从字符流中只读出前两个字符”go”时,第一个只出现一次的字符是”g”。当从该字符流中读出前六个字符“google”时,第一个只出现一次的字符是”l”。
解题思路 :
注意问题 :
补充知识点 :