welcome to my blog
剑指offer面试题20(java版):表示数值的字符串
要掌握非正则表达式的版本
题目描述
请实现一个函数用来判断字符串是否表示数值(包括整数和小数)。例如,字符串"+100",“5e2”,"-123",“3.1416"和”-1E-16"都表示数值。 但是"12e",“1a3.14”,“1.2.3”,"±5"和"12e+4.3"都不是。
第四次做; 核心: 1) 使用索引之前先检查索引是否越界 2)有效数字只有两种模式:A[.[B]][[e|EC]; .B[e|EC]
/*
核心: 有效数字的模式有两种:1)A[.[B]][e|EC] 2).B[e|EC]; 其中,A、C是整数,B是正整数
有A的话,有没有B都可以
没有A的话, 必须有B
*/
class Solution {
//扫描字符串时的索引
int i=0;
public boolean isNumber(String s) {
//input check
if(s==null || s.length()==0)
return false;
//去掉首尾的空字符
s = s.trim();
boolean A = scanInteger(s), B=false, C=false;
//判断是否有B; 使用索引时要确保索引不越界
if(i<s.length() && s.charAt(i)=='.'){
i++;
B = scanUnsignedInteger(s);
}
//判断是否有C
if(i<s.length() && (s.charAt(i)=='e' || s.charAt(i)=='E')){
i++;
C = scanInteger(s);
//如果存在e|E, 但是没有C, 说明不是数字
if(C==false)
return false;
}
//here, 说明C是合格的, 只需判断A和B的情况
//i必须扫描完整个字符串 && (A合格则B合不合格都可以, A不合格则B必须合格)
return i==s.length() && (A || B);
}
private boolean scanInteger(String s){
if(i<s.length() && (s.charAt(i)=='+' || s.charAt(i)=='-'))
i++;
return scanUnsignedInteger(s);
}
private boolean scanUnsignedInteger(String s){
//起始索引
int start = i;
while(i<s.length() && s.charAt(i)>='0' && s.charAt(i)<='9'){
i++;
}
//i>start说明扫描到了数字;
//i<=start说明没有扫描到数字, 此种情况说明要么start越界, 要么s.charAt(start)不是数字
return i > start;
}
}
笔记
- 正则表达式中\d表示数字, 将\d放在java的字符串中时,\表示转移字符而不是\本身的含义, 如果想让\表示本身的含义,需要写成\\
- "[e|E][±]?"表示第一个数是e或者E,第二个数是+或者-或者没有. 这里要清晰的是: 一个[]描述一个位置,两个[]描述两个位置. 体会[e|E][±]与[e|E±]的区别
- 有e|E则其后一定有整数,该整数可以带正负号
- 最开始用正则表达式的时候卡在一个位置, 因为我把小数点单独拿出来思考了,所以出现了整数部分和小数部分同时不出现,只剩小数点的问题,之所以没有发现要把小数点和小数部分的值作为整体一起考虑,是因为我没有想到另外一个情况: 小数点不出现,整数部分和小数部分同时出现. 想到这个例子后我应该就会觉得小数点应该和小数部分作为整体考虑
- 把小数点和小数部分的值作为整体一起考虑
- 不过使用正则表达式还有点问题, 就是e12这种会判断为true, 所以暂时别用正则表达式了
- 特殊例子:-.123, 这是true
- 特殊例子:1a3.14, 这是false
public class Solution {
public static boolean isNumeric(char[] str) {
String s = String.valueOf(str);
boolean result = s.matches("[+-]?\\d*(\\.\\d*)?([e|E][+-]?\\d+)?"); //把小数点和小数作为整体考虑: 一起出现或者一起不出现
return result;
}
}
第三次做, 独立的部分:e|E存在的话, 其后必须有整数; 核心:两种合理的模式; 返回值的写法
/*
核心, 数值以两种模式出现:
A[.[B]][e|EC]
.B[e|EC]
A和C表示整数
B表示正整数
出现e|E的时候,后面必须有整数
*/
public class Solution {
int index = 0;
public boolean isNumeric(char[] str) {
if(str==null || str.length==0)
return false;
boolean A = scanInteger(str);
boolean B = false, C = false;
if(index<str.length && str[index]=='.'){
index++;
B = scanUnsignedInteger(str);
}
if(index<str.length && (str[index]=='e'||str[index]=='E')){
index++;
C = scanInteger(str);
if(C==false)//出现了e|E, 则后面必须有整数, 没有就返回false
return false;
}
// 首先index必须走过每个char, 其次: 如果有A就返回true; 如果没有A则必须有B
return index == str.length && (A || B);
}
public boolean scanInteger(char[] str){
if(index<str.length && (str[index]=='+'||str[index]=='-'))
index++;
return scanUnsignedInteger(str);
}
public boolean scanUnsignedInteger(char[] str){
int start = index;
while(index<str.length && str[index]>='0' && str[index] <='9')
index++;
return index > start;
}
}
第二次做, 把数值分成三部分; 使用一个全局变量作为索引
/*
A[.[B]][e|EC]
.B[e|EC]
将数值分成三部分A,B,C
两大类对立的情况:
1)A,B这两部分必须有一个存在,也就是true
2)如果A,B都不存在,那么C必须存在
其他限制
如果有e|E的话C必须存在,否则不用再判断了,直接返回false, 因为12e这样的是错误的
*/
public class Solution {
public int index=0;
public boolean isNumeric(char[] str) {
if(str==null || str.length==0)
return false;
//
boolean A = scanInteger(str);
boolean B = false, C = false;
if(index < str.length && str[index] == '.' ){
index++;
B = scanUnsignedInteger(str);
}
if(index < str.length && (str[index]=='e' || str[index]=='E')){
index++;
C = scanInteger(str);
//有e|E的话,其后必须有整数,也就是C必须是true, 是false的话就直接返回false
if(C==false)
return C;
}
return (A || B) == true ? index==str.length : index==str.length && C;
}
public boolean scanInteger(char[] str){
if(index<str.length && (str[index]=='+'|| str[index]=='-'))
index++;
return scanUnsignedInteger(str);
}
public boolean scanUnsignedInteger(char[] str){
int start = index;
while(index<str.length && str[index]>='0' && str[index]<='9')
index++;
return index > start;
}
}
非正则表达式版代码
思路
- 写两个函数: 判断有符号整数, 判断无符号整数
- 出现的数字符合两种模式: 包含整数部分 A[.[B]][e|EC]; 不包含整数部分 .[B][e|EC]
- 写出判断ABC的代码
笔记
- 务必留意: result必须在||右边, 不能放在左边. 因为如果放在左边,同时result又是true, 那么就不会执行||右边的语句了
- result = scanUnsignedInteger(str) || result;
public class Solution {
private int index = 0;
public boolean isNumeric(char[] str) {
// input check
if(str.length < 1)
return false;
// execute
boolean result = scanInteger(str);
if(index < str.length && str[index] == '.'){
index++;
// 下面这句表示三种情况:整数部分和小数部分都存在; 整数部分存在,小数部分不存在; 整数部分不存在,小数部分存在
// 不允许出现正数和小数都不出现的情况
// 务必留意: result必须在||右边, 不能放在左边. 因为如果放在左边,同时result又是true, 那么就不会执行||右边的语句了
result = scanUnsignedInteger(str) || result;
}
if(index < str.length && (str[index] == 'e' || str[index] == 'E')){
index++;
result = result && scanInteger(str);
}
// 不仅要返回result, 还要确保已经判断完每个char
return result && (index == str.length);
}
public boolean scanInteger(char[] str){
if(index < str.length && (str[index] == '+' || str[index] == '-'))
index++;
return scanUnsignedInteger(str);
}
public boolean scanUnsignedInteger(char[]str){
int start = index;
while(index < str.length && str[index] >= '0' && str[index] <= '9'){
index++;
}
// index比start大, 说明扫描的部分有数字, 返回ture
// index等于start, 说明扫描的部分没有数字, 返回false
return start < index;
}
}