劍指offer-每日6題之第九天(java版)
原題連結:
第一題:把字串轉換成整數
題目描述
將一個字串轉換成一個整數(實現Integer.valueOf(string)的功能,但是string不符合數字要求時返回0),要求不能使用字串轉換整數的庫函式。
數值為0或者字串不是一個合法的數值則返回0。 輸入描述: 輸入一個字串,包括數字字母符號,可以為空 輸出描述:
如果是合法的數值表達則返回該數字,否則返回0
解析
將字串轉換成字元陣列,逐個比較,判斷是否是數字
若是,則更新sum=sum*10+a[i]-'0'
(程式碼中用了移位,計算比較快)
注意:可能數字字串有'+'
,'-'
所以用一個布林值標誌:負為false
public int StrToInt(String str) { if (str.equals("") || str.length() == 0) return 0; char[] a = str.toCharArray(); boolean temp = true; if (a[0] == '-') temp = false; int sum = 0; for (int i = (a[0] == '-' || a[0] == '+') ? 1 : 0; i < a.length; i++){ if (a[i] < 48 || a[i] > 57) return 0; sum = (sum<<1)+(sum<<3) + (a[i] & 0xf);//sum*10=sum*(2^1+2^3) } return temp ? sum : sum * -1; }
第二題:陣列中重複的數字
題目描述
在一個長度為n的數組裡的所有數字都在0到n-1的範圍內。
陣列中某些數字是重複的,但不知道有幾個數字是重複的。也不知道每個數字重複幾次。請找出陣列中任意一個重複的數字。
例如,如果輸入長度為7的陣列{2,3,1,0,2,5,3},那麼對應的輸出是第一個重複的數字2。
解析
方法一:
定義一個布林型陣列,陣列的key為第i個位置的元素,value代表這個元素是否訪問過,初始化為false(都是未訪問的)
public boolean duplicate(int numbers[],int length,int [] duplication) { boolean[] k = new boolean[length]; for (int i = 0; i < k.length; i++) { if (k[numbers[i]] == true) { duplication[0] = numbers[i]; return true; } k[numbers[i]] = true; } return false; }
方法二:
調整陣列的位置,使得第i個位置存放值為i的元素。
遍歷陣列,判斷當前位的值和下標是否相等:
- 若相等,則遍歷下一位
- 若不等,則將當前位置i上的元素和numbers[i]位置上的元素比較:若它們相等,則成功!若不等,則將它們兩交換
- 若當前位置i的值也為i,將i向後移一位,重複步驟1,否則,重複步驟2(換完之後numbers[i]位置上的值和它的下標是對應的,但i位置上的元素和下標並不一定對應)
public boolean duplicate(int numbers[],int length,int [] duplication) { if(numbers == null || length <= 0) { return false; } for(int i = 0; i < length; i++) { while(numbers[i] != i) { if(numbers[i] == numbers[numbers[i]]) { duplication[0] = numbers[i]; return true; } int temp = numbers[i]; numbers[i] = numbers[temp]; numbers[temp] = temp; } } return false; }
第三題:構建乘積陣列
題目描述
給定一個數組A[0,1,…,n-1],請構建一個數組B[0,1,…,n-1],其中B中的元素B[i]=A[0]A[1]…*A[i-1]A[i+1]…*A[n-1]。不能使用除法。
解析
劍指的思路:
如圖:B[i]的值是矩陣中每行的乘積。
- 求B[i]可以先求B[i]的一部分(下三角,從上往下計算):
初始化B[i]=1
B[i]=B[i-1]*A[i-1]
- 然後求B[i]的另一部分(上三角,從下往上計算):
定義一個變數temp累計上三角的值
初始化temp=1
temp=temp*A[i+1]
然後將這部分值乘到B[i]中,即B[i]=B[i]*temp
public int[] multiply(int[] A) {
int length = A.length;
int[] B = new int[length];
if(length != 0 ){
B[0] = 1;
//計算下三角連乘
for(int i = 1; i < length; i++){
B[i] = B[i-1] * A[i-1];
}
int temp = 1;
//計算上三角
for(int j = length-2; j >= 0; j--){
temp *= A[j+1];
B[j] *= temp;
}
}
return B;
}
第四題:正則表示式匹配
題目描述
請實現一個函式用來匹配包括’.‘和’‘的正則表示式。模式中的字元’.‘表示任意一個字元,而’'表示它前面的字元可以出現任意次(包含0次)。
在本題中,匹配是指字串的所有字元匹配整個模式。例如,字串"aaa"與模式"a.a"和"abaca"匹配,但是與"aa.a"和"ab*a"均不匹配
解析
分兩種情況:
- 當模式中第2個字元是
'*'
1.1 字串第1個跟模式第1個不匹配:將模式串後移2位
1.2 字串第1個跟模式第1個匹配
1.2.1 將模式後移2位
1.2.2 字串後移1位,模式串後移2位
1.2.3 字串後移1位,模式串不變 - 當模式中第2個字元不是
'*'
2.1 字串第1個跟模式第1個匹配:兩串都向後移1位,匹配子串
2.2 直接返回false
public class Solution {
public boolean match(char[] str, char[] pattern){
if(str==null||pattern==null)
return false;
int strindex=0;
int patindex=0;
return matchCore(str,strindex,pattern,patindex);
}
public boolean matchCore(char[] str,int strindex, char[] pattern,int patindex){
if(strindex==str.length && patindex==pattern.length)
return true;
else if(strindex!=str.length && patindex==pattern.length)
return false;
//模式第2個是*,且字串第1個跟模式第1個匹配,分3種匹配模式;如不匹配,模式後移2位
if(patindex+1<pattern.length && pattern[patindex+1]=='*'){
if((strindex != str.length &&pattern[patindex]=='.')||(strindex != str.length && pattern[patindex] == str[strindex]))
return matchCore(str,strindex+1,pattern,patindex+2)//匹配一次
|| matchCore(str,strindex+1,pattern,patindex)//匹配多次
|| matchCore(str,strindex,pattern,patindex+2);//x*被忽略
else
return matchCore(str,strindex,pattern,patindex+2);//如 aa b*aa
}
//模式第2個不是*,且字串第1個跟模式第1個匹配,則都後移1位,匹配餘下的,否則直接返回false
if((strindex != str.length &&pattern[patindex]=='.')||(strindex != str.length && pattern[patindex] == str[strindex])){
return matchCore(str,strindex+1,pattern,patindex+1);
}
return false;
}
}
第五題:表示數值的字串
題目描述
請實現一個函式用來判斷字串是否表示數值(包括整數和小數)。例如,字串"+100",“5e2”,"-123",“3.1416"和”-1E-16"都表示數值。
但是"12e",“1a3.14”,“1.2.3”,"±5"和"12e+4.3"都不是。
解析
- 表示數值的字串遵循的模式:
A[.[B]][e|EC]
或者.B[e|EC]
(A可以沒有,如.123
為0.123
)
A->整數部分(以'+'
或'-'
開頭的0-9的字串)
B->小數部分(0-9的字串)
C->指數部分(以'+'
或'-'
開頭的0-9的字串)
public class Solution {
private int index=0;
public boolean isNumeric(char[] str) {
if(str==null || str.length==0)
return false;
boolean flag=scanInteger(str);
if(index<str.length&&str[index]=='.'){
index++;
//前後有一個位置有數字就可以 eg:0.123 23.0 23.4
//為了防止短路,必須將函式呼叫寫到前面
flag=scanUnsignedInteger(str)||flag;
}
if(index<str.length&&(str[index]=='e'||str[index]=='E')){
index++;
flag=flag && scanInteger(str);//前後都要有數字 14E18
}
return flag && index==str.length;
}
public boolean scanUnsignedInteger(char[] str){
int before=index;
while(index<str.length && str[index]>='0'&& str[index]<='9')
index++;
return index>before;
}
public boolean scanInteger(char[] str){
if(index<str.length &&(str[index]=='+'||str[index]=='-'))
index++;
return scanUnsignedInteger(str);
}
}
第六題:字元流中第一個不重複的字元
題目描述
請實現一個函式用來找出字元流中第一個只出現一次的字元。例如,當從字元流中只讀出前兩個字元"go"時,第一個只出現一次的字元是"g"。當從該字元流中讀出前六個字元“google"時,第一個只出現一次的字元是"l"。
輸出描述:
如果當前字元流沒有存在出現一次的字元,返回#字元。
解析
- 字元只能一個接著一個從字元流中讀出來。所以可以定義一個容器儲存字元在字元流中的位置。選用雜湊表,將字元的ASCⅡ碼作為key,value為字元在字元流中的位置。
- 初始化雜湊表中每個value為-1(設定一個小於0的數代表沒有在字元流中遇到過這個字元),當一個字元第一次從字元流讀出來時,將value更新為字元流中的位置。當不是第一次從字元流讀出來時(value>0,即已經更新為字元流的下標了),將value更新為一個特殊值-2(設定一個小於0的且不等於-1的數作為標識都可以)
- 此時,尋找第一個不重複的字元,即遍歷雜湊表,找到value>=0且value最小的key
public class Solution {
public int occurrence[]=new int[256];
public int index;
public Solution(){
for(int i=0;i<256;++i){
occurrence[i]=-1;
}
index=0;
}
//Insert one char from stringstream
public void Insert(char ch)
{
if(occurrence[ch]==-1)
occurrence[ch]=index;
else if(occurrence[ch]>=0)
occurrence[ch]=-2;
index++;
}
//return the first appearence once char in current stringstream
public char FirstAppearingOnce()
{
char ch='#';
int minIndex=Integer.MAX_VALUE;
for(int i=0;i<256;++i){
if(occurrence[i]>=0&&occurrence[i]<minIndex){
ch=(char)i;
minIndex=occurrence[i];
}
}
return ch;
}
}