【题目】LeetCode(8): String to Integer (atoi)
URL: https://leetcode.com/problems/string-to-integer-atoi/
【描述】
Implement atoi to convert a string to an integer.
Hint: Carefully consider all possible input cases. If you want a challenge, please do not see below and ask yourself what are the possible input cases.
Notes: It is intended for this problem to be specified vaguely (ie, no given input specs). You are responsible to gather all the input requirements up front.
【中文描述】
给一个字符串,要求转成integer。就这么一句话,不过有hint:
需要考虑各种可能的情况。
————————————————————————————————————————————————————————————
【初始思路】
题目也很直接的说,就是故意说这个模糊的,就是要考验你对于各种情况考虑的周不周全。但是个人感觉这个题设计还是有点问题。既然你不想让大家看下面的提示内容,那你就还是应该是多少说清楚,比如最明显的几种情况,字符串为空怎么处理,为null怎么处理,overflow了怎么处理。
所有这些东西,只能点开下面的详细提醒,才能看到。然后,先根据自己的理解,整理了一部分:
(1) if the string is " " or "" or null, should return 0
(2) str = "3124123fah1;kjf;lasdf", should return 3124123
(3) str = " fafa123fnaf21j", should return 0
(4) str = " -312341", should return -312341
(5) str = "ajklfla;hf;f", should return 0
然后根据这几个例子开撸。
首先,我们直接把str.trim()一下,然后情况就简单了。如果第一个字符是正负号或者数字,就接着从第二个字符开始判断。如果第一个字符直接就是特殊字符,直接return 0.
其次,遍历是肯定的,for(int i =0;i<str.length();i++),然后charAt(i)取出每个字符
第三,遇到'+'或者'-'怎么处理。由于之前已经判断过第一个字符是不是正负号,所以从第二个字符开始无需再判断正负号,全部按照非法字符对待;
第四,遇到非数字字符怎么处理。这个相对简单一点,如果当前是第二位,那么由于第一位有可能是数字,所以需要考虑第一位的情况。
if(第一位是数字) break;
else { //说明第一位不是数字,那只能是正负号
return 0;
}
如果当前不是第二位,那就简单,直接break就好了。
第五,剩下的可能性,就只有数字了。定义一个累积器,把遇到的数字加起来就行了。
这就够了么?
溢出溢出溢出!!!重要的事情说三遍!
如果溢出怎么办?还好题目要求很人性化,只要溢出就返回Integer.MAX_VALUE 或者 Integer.MIN_VALUE。那溢出的判断就简单了!
累积的过程中,给一个记位器count,每加一位数字,count++。每次在遇到数字需要累加之前,先count++,然后判断是否>=9(读者请考虑为何是9),如果<9,肯定不会溢出,累加然后continue即可。
如果 count >= 9, 就有可能溢出。我们让累加器先累加出结果,然后把结果和MAXVALUE,MINVALUE做比较,如果溢出,直接返回对应的MAXVALUE或者MINVALUE即可。
同时,由于有可能有正负号,所以在累加结果出来后,需要根据正负号情况,分情况判断。
最后,如果溢出判断通过了,循环结束了,剩下的就是输出累加结果。同样的,根据正负号判断一下,如果没有正负号,按照正数对待,输出即可。
【Show me the Code!!!】
1 if(str == null || str.equals("")) { 2 return 0; 3 } 4 str = str.trim(); 5 char tmp; 6 boolean signflag = false;//是否之前已经有正负号的标志位 7 long result = 0L;//用long做累加器,可以防止溢出 8 char sign = ' ';//记录正负号 9 int count = 0;//位数计数器,用来做溢出可能的判断 10 if(str.charAt(0) == '+' || str.charAt(0) == '-') { 11 sign = str.charAt(0); 12 signflag = true; 13 } else if(!Character.isDigit(str.charAt(0))) { 14 return 0; 15 } else { //第一位是数字 16 result = str.charAt(0) - '0'; 17 } 18 for(int i = 1; i < str.length(); i++){ 19 tmp = str.charAt(i); 20 21 //情况1, 遇到非数字的字符,因为之前第一位检查过是否为正负号, 所以,这里如果不是数字,就要看第一位是什么 22 if(!Character.isDigit(tmp)) { 23 if(i==1) { 24 //第一位后紧跟着非数字字符 25 if(Character.isDigit(str.charAt(0))){//如果第一位是数字,那就break. 26 break; 27 } 28 return 0; 29 } else { 30 //不是紧跟着出现,说明前面已经有数字 31 break; 32 } 33 } else {//遇到数字 34 count++; 35 result = result * 10 + (tmp - '0'); 36 if(count >= 9){//数字位数达到10位,需要考虑溢出问题 37 if(signflag && sign == '-') { //有符号,且为负 38 if(result * -1 < Integer.MIN_VALUE) return Integer.MIN_VALUE; 39 } 40 if(signflag && sign == '+') { //有符号,且为正 41 if(result > Integer.MAX_VALUE) return Integer.MAX_VALUE; 42 } 43 //这个可能性不能漏了,没有符号,按照正的对待 44 if(!signflag && result > Integer.MAX_VALUE) {return Integer.MAX_VALUE ;} 45 } 46 } 47 } 48 //程序走到这里,说明没有溢出,我们也累加得到了数字result, 但是result是无符号的,所以需要考虑正负号 49 if(signflag && sign == '-') { //如果有符号且为负,转换符号输出 50 return (int)result * -1; 51 } 52 if(signflag && sign == '+') { //如果有符号且为正,直接输出 53 return (int)result; 54 } 55 return (int)result; //这种情况是捡漏的,如果无符号,走到这里,直接输出
【反思 】
遇到这种需要考虑N多种情况的题,不要一个情况一个情况分别对待。要懂得归并,很多问题,其实合并后是一个问题。
同时,要懂总结规律。例如本题中,由于一开始的' '全都可以跳过,所以可以直接trim(),去掉他们的干扰。trim后,第一个字符要么是正负号,要么是数字才可以接受。否则直接return 0。通过trim的操作,直接在后面的循环里可以不用再考虑' '的情况,遇到非数字只需要考虑当前情况即可。