题目描述:把字符串转换成整数
将一个字符串转换成一个整数(实现Integer.valueOf(string)的功能,但是string不符合数字要求时返回0),要求不能使用字符串转换整数的库函数。 数值为0或者字符串不是一个合法的数值则返回0。
这个解题算是考虑非常全面了,实现也很清晰,所以就摘录下来,以时时观摩。
package offer;
import java.util.Arrays;
public class Solution {
public static boolean flag;
public static int StrToInt(String str) {
//flag判断是因为输入字符串0返回0 还是错误情况下直接返回0
flag = false;
//判断输入是否合适
if(str == null || str.trim().equals("")) {
flag = true;
return 0;
}
//symbol=0,说明该数为正数;symbol=1,该数为负数;
//start用来区分第一位是否为符号位
int symbol = 0;
int start = 0;
char[] chars = str.toCharArray();
if(chars[0] == '+') {
start = 1;
}else if(chars[0] == '-') {
start = 1;
symbol = 1;
}
int result = 0;
for(int i=start;i<chars.length;i++) {
if(chars[i] > '9' || chars[i] < 0) {
flag = true;
return 0;
}
int sum = result*10 + (int)(chars[i]-'0');
//举个例子,3*10+5=35,如果(35-5)/10不等于3,那么就产生了溢出
if((sum - (int)(chars[i]-'0'))/10 != result) {
flag = true;
return 0;
}
result = result * 10 + (int)(chars[i]-'0');
}
result = (int) Math.pow(-1, symbol) * result;
return result;
}
public static void main(String[] args) {
String str = "8797899";
System.out.println(StrToInt(str));
}
}
题目描述:数组中重复的数字
在一个长度为n的数组里的所有数字都在0到n-1的范围内。 数组中某些数字是重复的,但不知道有几个数字是重复的。也不知道每个数字重复几次。请找出数组中任意一个重复的数字。 例如,如果输入长度为7的数组{2,3,1,0,2,5,3},那么对应的输出是第一个重复的数字2。
这是我个人的解法,没考虑太多,就是i外循环控制每次开始比较的起点,i之前的是已经与后面的数组比较了,没有重复的。然后i向后移,作为到当前的比较数,一个一个地跟后面的比较。但最后提交时,确总是显示我的代码测试通过率为83.3%,让人头大。。。
public class Solution {
public static boolean duplicate(int numbers[],int length,int [] duplication) {
boolean flag = false;
//代码1
if(length <= 2) return false;
int index = 0;
for(int i = 0;i<length-1;i++){
index = i;
for(int j = i;j<length-1;j++){
if(numbers[j+1] == numbers[index]){
duplication[0] = numbers[index];
flag = true;
return flag;
}
}
}
return flag;
}
public static void main(String[] args) {
int[] chars = {2,3,1,0,2,5,3};
int[] dupChar = new int[chars.length];
System.out.println(duplicate(chars,chars.length,dupChar));
}
}
后来添加了判断验证“一个长度为n的数组里的所有数字都在0到n-1的范围内”这个条件,代码得以通过,不过,这不是写在题目里了吗?我的理解是这个条件是默认的呢。。。好吧,题目就是上帝,自己多多考虑,写全面了,才是硬道理。于是在上面 代码1 处添加以下判别代码:
for(int i=0;i<length;i++){
if(numbers[i] < 0 || numbers[i] > (length-1)) return flag;
}
有一种所谓高级的方法,利用了哈希的特性,但不需要额外的存储空间。
主要算法如下:
- 把当前序列当成是一个下标和下标对应值时相同的数组;
- 遍历数组,判断当前位的值和下标是否相等;
- 若相等,则遍历下一位;若不等,则将当前位置i上的元素和a[i]位置上的元素比较;
- 若它们相等,则成功;若不等,则将它们俩交换。换完之后a[i]位置上的值和它的下标是对应的,但i位置上的元素和下标不一定对应;重复3的操作,知道当前位置i的值也为i,将i后移一位,再重复2。
链接:https://www.nowcoder.com/questionTerminal/623a5ac0ea5b4e5f95552655361ae0a8
来源:牛客网
public boolean duplicate(int array[],int length,int [] duplication) {
if ( array==null ) return false;
// 判断数组是否合法(每个数都在0~n-1之间)
for ( int i=0; i<length; i++ ) {
if ( array[i]<0 || array[i]>length-1 ) {
return false;
}
}
// key step
for( int i=0; i<length; i++ ){
while( i!=array[i] ){
if ( array[i] == array[array[i]] ) {
duplication[0] = array[i];
return true;
}
int temp = array[i];
array[i] = array[array[i]];
array[array[i]] = 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]=A[0]*A[1]*...*A[i-1]A[i]*A[i+1]*...*A[n-1]!!!
- 实现算法太简单,当然简单不是错,而是复杂度太高,一点技术含量也没有。
public class Solution {
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;
}
}
这个就有点档次了,但带来的问题就是,有点难以理解,按照矩阵里的说法就是,上三角,和下三角。当i为1的时候,下三角计算只有一个值,但上三角确是有n-1个值连乘。其实也可以这么理解,将一整个B[i]的连乘分为两步,第一步,先连乘下三角的部分,将处于下三角的部分的值连乘为目前的B[i]。然后再连乘上三角部分的值,这时就是下三角连乘过后的目前的B[i],依次乘以上三角的值,最后算完了,就是完整的B[i]。
从计算下三角和上三角的循环中我们就看到,下三角计算是已知B[0],i从1一直到n-1,即B[i]下三角元素全部乘了之后赋值B[i]。
从计算上三角和循环可以看到,是已知了B[n-1]肯定为1,然后从n-2一直到0,即B[i]上三角的元素全部乘了之后赋值B[i]。
题目描述:正则表达式匹配
请实现一个函数用来匹配包括'.'和'*'的正则表达式。模式中的字符'.'表示任意一个字符,而'*'表示它前面的字符可以出现任意次(包含0次)。 在本题中,匹配是指字符串的所有字符匹配整个模式。例如,字符串"aaa"与模式"a.a"和"ab*ac*a"匹配,但是与"aa.a"和"ab*a"均不匹配。
感觉这道题的精髓在于,选择谁主动、谁被动,拿出谁去匹配谁。还有,就是把这些复杂的问题分分清楚,进行共性的抽象以及递归。
我个人做的方法,测试case通过率为6.67%,汗。。。分析自己主要是没把主要问题和次要问题,分分清楚。对于 if 和 else 以及各种逻辑的判断,没有把头绪理清楚,问题分类好。比如,对于匹配模式中的字符,有三种:
- 普通字符
- '.'
- '*'
所有匹配字符中的组合,均是这三中或组合,具有实际意义,可以独立具有意义的罗列如下(普通字符以a代替):a . a*
即,所有的 '.' 和 a 都可以自己匹配字符,但对于 '*' 自己,却不具有任何意义,必须与 a 组合,且紧跟 a 之后,形成 'a*'。
那么问题就可以简化为两种了:
- a 或 '.'
- 'a*'
进一步地表达就是,pattern 的下一个字符是否为 '*'。那么对于匹配的算法就是:
- 字符串中拿出第一个字符和模式中的字符去匹配
- 如果pattern下一个字符不为 '*' ,那就比较简单。直接对当前字符进行匹配。如果匹配成功,继续匹配下一个;算法继续从1开始;匹配失败,则直接返回false。需要注意的是,匹配成功,有两种,一是字符相同;二是pattern中的当前字符为 '.' ;字符相同到没啥,但如果是第二种情况的话,还要考虑str中的边界问题。
- 如果如果pattern下一个字符是 '*' ,那就会稍微复杂一点,因为 '*' 可以代表0个或多个。
- 当 '*' 匹配0个字符时,即当前字符串字符与 '*' 前的字符不匹配,因此,str保持当前字符不变,pattern当前字符向后移动两位。注意:此时pattern指向的还是在 '*' 之前的字符上,没有匹配上,就把 'a*' 忽略掉,因此从目前的a上向后移动两位。
- 当 '*' 匹配一个或多个时,str当前字符向后移动一位,pattern当前字符不变。注意:这里呢,将匹配一个和多个看做同一种情况,因为,匹配一个是,str向后移动一位,此是pattern当前字符不变,情况跟上上一次相同,再移再相同。直到出现str当前字符与pattern字符不匹配,那么就进入4的情况了。
- 之后,就是撸代码了。
/**
* 正则表达式匹配
* 当模式中的第二个字符不是 '*' 时:
* 1、如果字符串第一个字符和模式中的第一个字符相匹配,那么字符串和模式都后移一个字符,然后匹配剩余的(递归)。
* 2、如果字符串第一个字符和模式中的第一个字符相不匹配,直接返回false。
* 当模式中的第二个字符是 '*' 时:
* 如果字符串的第一个字符跟模式第一个字符不匹配,则模式后移2个字符,继续匹配。
* 如果字符串第一个字符跟模式第一个字符匹配,可以有3中匹配方式:
* 1、模式向后移动2个字符,相当于 a* 被忽略。
* 2、字符串向后移动1个字符,模式向后移动1个字符;
* 3、字符串后移1个字符,模式不变,即继续匹配字符下一位,因为 * 可以匹配多位。
*
*/
public class Solution {
public boolean match(char[] str, char[] pattern) {
if (str == null || pattern == null) {
return false;
}
int strIndex = 0;
int patternIndex = 0;
return matchCore(str, strIndex, pattern, patternIndex);
}
public boolean matchCore(char[] str, int strIndex, char[] pattern, int patternIndex) {
//有效性检验:str到尾,pattern到尾,匹配成功
if (strIndex == str.length && patternIndex == pattern.length) {
return true;
}
//pattern先到尾,匹配失败
if (strIndex != str.length && patternIndex == pattern.length) {
return false;
}
//模式第2个是*,且字符串第1个跟模式第1个匹配,分3种匹配模式;如不匹配,模式后移2位
if (patternIndex + 1 < pattern.length && pattern[patternIndex + 1] == '*') {
if ((strIndex != str.length && pattern[patternIndex] == str[strIndex]) || (pattern[patternIndex] == '.' && strIndex != str.length)) {
return matchCore(str, strIndex, pattern, patternIndex + 2)//模式后移2,视为x*匹配0个字符
|| matchCore(str, strIndex + 1, pattern, patternIndex + 2)//视为模式匹配1个字符
|| matchCore(str, strIndex + 1, pattern, patternIndex);//*匹配1个,再匹配str中的下一个
} else {
return matchCore(str, strIndex, pattern, patternIndex + 2);
}
}
//模式第2个不是*,且字符串第1个跟模式第1个匹配,则都后移1位,否则直接返回false
if ((strIndex != str.length && pattern[patternIndex] == str[strIndex]) || (pattern[patternIndex] == '.' && strIndex != str.length)) {
return matchCore(str, strIndex + 1, pattern, patternIndex + 1);
}
return false;
}
}
题目描述:表示数值的字符串
请实现一个函数用来判断字符串是否表示数值(包括整数和小数)。例如,字符串"+100","5e2","-123","3.1416"和"-1E-16"都表示数值。 但是"12e","1a3.14","1.2.3","+-5"和"12e+4.3"都不是。
先贴上自己的拙作,case通过率为6.67%。。。。。。
public class Solution {
public boolean isNumeric(char[] str) {
if(str.length == 0) return false;
int start = 0;
if(str[0] == '+' || str[0] == '-'){
start = 1;
}
while(str[start] >= '0' && str[start] <= '9'){
if(start == str.length-1) return true;
start++;
}
if(str[start] == '.'){
for(;start<str.length;start++){
if(!(str[start] >= '0' && str[start] <= '9'))
return false;
}
}else if(str[start] == 'e' || str[start] == 'E'){
if(start+1<str.length &&(str[start+1] == '+' || str[start+1] == '-')){
start += 1;
}
for(;start<str.length;start++){
if(!(str[start] >= '0' && str[start] <= '9'))
return false;
}
}else return false;
return true;
}
}
果真是if else到底型人才!!!
参考别人解法:
正则表达式解法
//正则表达式解法
public class Solution {
public boolean isNumeric(char[] str) {
String string = String.valueOf(str);
return string.matches("[\\+\\-]?\\d*(\\.\\d+)?([eE][\\+\\-]?\\d+)?");
}
}
/*
以下对正则进行解释:
[\\+\\-]? -> 正或负符号出现与否
\\d* -> 整数部分是否出现,如-.34 或 +3.34均符合
(\\.\\d+)? -> 如果出现小数点,那么小数点后面必须有数字;
否则一起不出现
([eE][\\+\\-]?\\d+)? -> 如果存在指数部分,那么e或E肯定出现,+或-可以不出现,
紧接着必须跟着整数;或者整个部分都不出现
*/
正常解答:
public class Solution {
private int index = 0;
public boolean isNumeric(char[] str) {
if (str.length < 1)
return false;
boolean flag = scanInteger(str);
if (index < str.length && str[index] == '.') {
index++;
flag = scanUnsignedInteger(str) || flag;
}
if (index < str.length && (str[index] == 'E' || str[index] == 'e')) {
index++;
flag = flag && scanInteger(str);
}
return flag && index == str.length;
}
private boolean scanInteger(char[] str) {
if (index < str.length && (str[index] == '+' || str[index] == '-') )
index++;
return scanUnsignedInteger(str);
}
private boolean scanUnsignedInteger(char[] str) {
int start = index;
while (index < str.length && str[index] >= '0' && str[index] <= '9')
index++;
return start < index; //是否存在整数
}
}