什么是面向测试编程?试试本题就知道了!
NO.65 有效数字 困难
没有get到这道题目的点,这样的题目很搞心态。尤其是看了作者的解答之后更懵逼了,作者使用了责任链的设计模式,从来没想到解算法题还能用到设计模式。大牛的思维方式就适合我不一样,解个算法题都能考虑到扩展性和复用性。
思路一:暴力法
按序遍历字符串,逐位判断是否合法。注意要去除首尾空格。
这种方法就是比较恶心,很容易有考虑不到的情况。
而且测试用例中有:”.1”、”.2”、”+.8”、”46.”、”2e0”等等,预期输出都是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
| public boolean isNumber(String s) { if (s==null)return false; s = s.trim(); if (s.length()==0)return false; boolean pointSeen = false; boolean eSeen = false; boolean numberSeen = false; boolean numberAfterE = true; for(int i=0; i<s.length(); i++) { if('0' <= s.charAt(i) && s.charAt(i) <= '9') { numberSeen = true; numberAfterE = true; } else if(s.charAt(i) == '.') { if(eSeen || pointSeen) { return false; } pointSeen = true; } else if(s.charAt(i) == 'e') { if(eSeen || !numberSeen) { return false; } numberAfterE = false; eSeen = true; } else if(s.charAt(i) == '-' || s.charAt(i) == '+') { if(i != 0 && s.charAt(i-1) != 'e') { return false; } } else { return false; } } return numberSeen && numberAfterE; }
|
时间复杂度:O(n)
写完暴力法,忍不住给下面这个骚操作点了个赞 : )
1 2 3 4 5 6 7
| class Solution: def isNumber(self, s: str) -> bool: try: key=float(s) return True except: return False
|
思路二:确定有限自动机(DFA)
下面搬运自leetcode社区windliang,我不是大佬,我只是大佬的搬运工。
先画出状态转换图:
如上图,从 0 开始总共有 9 个状态,橙色代表可接受状态,也就是表示此时是合法数字。总共有四大类输入,数字,小数点,e 和 正负号。我们只需要将这个图实现就够了。
这种方式思路清晰多了,但是之前没有接触过这种方法实现起来还是很生疏的。
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 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87
| public boolean isNumber(String s) { int state = 0; s = s.trim(); for (int i = 0; i < s.length(); i++) { switch (s.charAt(i)) { case '+': case '-': if (state == 0) { state = 1; } else if (state == 4) { state = 6; } else { return false; } break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': switch (state) { case 0: case 1: case 2: state = 2; break; case 3: state = 3; break; case 4: case 5: case 6: state = 5; break; case 7: state = 8; break; case 8: state = 8; break; default: return false; } break; case '.': switch (state) { case 0: case 1: state = 7; break; case 2: state = 3; break; default: return false; } break; case 'e': switch (state) { case 2: case 3: case 8: state = 4; break; default: return false; } break; default: return false;
} } return state == 2 || state == 3 || state == 5 || state == 8; }
|
时间复杂度:O(n)
思路三:责任链模式
解法二看起来已经很清晰明了了,只需要把状态图画出来,然后实现代码就很简单了。但是缺点是,如果状态图少考虑了东西,再改起来就会很麻烦。
这里作者提出来,利用责任链的设计模式,会使得写出的算法扩展性以及维护性更高。这里用到的思想就是,每个类只判断一种类型。比如判断是否是正数的类,判断是否是小数的类,判断是否是科学计数法的类,这样每个类只关心自己的部分,出了问题很好排查,而且互不影响。
虽然代码变多了,但是维护性,扩展性变的很强了。比如,题目新增了一种情况,”0x123” 16 进制也算是合法数字。这样的话,解法一和解法二就没什么用了,完全得重新设计。但对于解法三,我们只需要新增一个类,专门判断这种情况,然后加到执行者的数组里就够了,很牛逼!
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 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254
| interface NumberValidate { boolean validate(String s); }
abstract class NumberValidateTemplate implements NumberValidate{
public boolean validate(String s) { if (checkStringEmpty(s)) { return false; }
s = checkAndProcessHeader(s);
if (s.length() == 0) { return false; }
return doValidate(s); }
private boolean checkStringEmpty(String s) { if (s.equals("")) { return true; }
return false; }
private String checkAndProcessHeader(String value) { value = value.trim();
if (value.startsWith("+") || value.startsWith("-")) { value = value.substring(1); }
return value; }
protected abstract boolean doValidate(String s); }
class IntegerValidate extends NumberValidateTemplate{
protected boolean doValidate(String integer) { for (int i = 0; i < integer.length(); i++) { if(Character.isDigit(integer.charAt(i)) == false) { return false; } }
return true; } }
class SienceFormatValidate extends NumberValidateTemplate{
protected boolean doValidate(String s) { s = s.toLowerCase(); int pos = s.indexOf("e"); if (pos == -1) { return false; }
if (s.length() == 1) { return false; }
String first = s.substring(0, pos); String second = s.substring(pos+1, s.length());
if (validatePartBeforeE(first) == false || validatePartAfterE(second) == false) { return false; }
return true; }
private boolean validatePartBeforeE(String first) { if (first.equals("") == true) { return false; }
if (checkHeadAndEndForSpace(first) == false) { return false; }
NumberValidate integerValidate = new IntegerValidate(); NumberValidate floatValidate = new FloatValidate(); if (integerValidate.validate(first) == false && floatValidate.validate(first) == false) { return false; }
return true; }
private boolean checkHeadAndEndForSpace(String part) {
if (part.startsWith(" ") || part.endsWith(" ")) { return false; }
return true; }
private boolean validatePartAfterE(String second) { if (second.equals("") == true) { return false; }
if (checkHeadAndEndForSpace(second) == false) { return false; }
NumberValidate integerValidate = new IntegerValidate(); if (integerValidate.validate(second) == false) { return false; }
return true; } }
class FloatValidate extends NumberValidateTemplate{
protected boolean doValidate(String floatVal) { int pos = floatVal.indexOf("."); if (pos == -1) { return false; }
if (floatVal.length() == 1) { return false; }
NumberValidate nv = new IntegerValidate(); String first = floatVal.substring(0, pos); String second = floatVal.substring(pos + 1, floatVal.length());
if (checkFirstPart(first) == true && checkFirstPart(second) == true) { return true; }
return false; }
private boolean checkFirstPart(String first) { if (first.equals("") == false && checkPart(first) == false) { return false; }
return true; }
private boolean checkPart(String part) { if (Character.isDigit(part.charAt(0)) == false || Character.isDigit(part.charAt(part.length() - 1)) == false) { return false; }
NumberValidate nv = new IntegerValidate(); if (nv.validate(part) == false) { return false; }
return true; } }
class NumberValidator implements NumberValidate {
private ArrayList<NumberValidate> validators = new ArrayList<NumberValidate>();
public NumberValidator() { addValidators(); }
private void addValidators() { NumberValidate nv = new IntegerValidate(); validators.add(nv);
nv = new FloatValidate(); validators.add(nv);
nv = new SienceFormatValidate(); validators.add(nv); }
@Override public boolean validate(String s) { for (NumberValidate nv : validators) { if (nv.validate(s) == true) { return true; } }
return false; }
} public boolean isNumber(String s) { NumberValidate nv = new NumberValidator(); return nv.validate(s); }
|
本人菜鸟,有错误请告知,感激不尽!
更多题解和学习记录博客:博客、github