题目链接:https://leetcode.cn/problems/ba-zi-fu-chuan-zhuan-huan-cheng-zheng-shu-lcof/
1. 题目介绍(67. 把字符串转换成整数)
写一个函数 StrToInt,实现把字符串转换成整数这个功能。不能使用 atoi 或者其他类似的库函数。
【测试用例】:
【相关题目】:
- 注意:本题与主站 8. 字符串转换整数 (atoi) 题目相同。
2. 题解
2.1 库函数
【解题思路】:
在 Java 中常使用Integer.parseInt()
以及Integer.valueOf()
将一个字符串变量转化成一个整型变量,它俩的区别就在于:
- Integer.parseInt(s) 是把字符串s解析成有符号的
int
基本类型;- Integer.valueOf(s) 是把字符串s解析成
Integer
对象类型,且在Integer 类中还有一个内部的缓存类IntegerCache
,它默认缓存了[-128, 127]的Integer值. 如果使用的是Integer.valueOf(s),它就会去检查这个数字是否在 [-128, 127] 这个范围内,如果在这个范围内,则直接在 IntegerCache 中取值;如果不在,则新创建一个Integer对象,同时也是为什么Integer 用 “== ” 比较时127相等而128不相等的原因。所以,一般情况下,如果我们只是需要一个 int 值,parseInt 是合适的,而且效率要高,用valueOf 就属于多此一举了,性能会下降,同样Integer, Long, Double, Float 都是一样的道理。
……
缺点:使用库函数转化字符串,要求字符串中只能为在类型范围内的正确数字才行,如果出现了空格、非0~9之间的其它字符均会报错。
- 数字越界:
- 字符串中存在空格:
- 字符串中非0~9之间的其它字符:
……
更多内容可参考:
[1] Integer.parseInt(s)与Integer.valueOf(s)的区别详解
[2] Integer.valueOf和Integer.parseInt区别
[3] [概念理解] Java中parseXXX和valueOf,toString的区别
class Solution {
// Solution1: 库函数
public int strToInt(String str) {
String s = str.trim();
// 将字符串转成整数
// Integer.valueOf 返回值是 Integer 类型
// Integer.parseInt 返回值是 int 类型
int a = Integer.parseInt(s);
int b = Integer.valueOf(s); // 自动拆箱,相当于调用了Integer中的 intValue()方法
// 将整数转换成字符串
String s1 = String.valueOf(b);
System.out.println(s1);
return b;
}
}
2.2 自定义 strToInt() 函数 – O(n) ⭐
时间复杂度 O(n),空间复杂度 O(n)
【解题思路】:
题目不难,但有很多细节需要注意,如下所示:
- 首尾空格:首尾空格最好解决,直接使用库函数
trim()
即可删除首尾空格;- 符号位:三种情况,即 ‘’+‘’ , ‘‘−’’ , ''无符号" ;定义一个变量
sign
用来表示符号位,返回前判断正负即可- 非数字字符:遇到首个非数字的字符时,应立即返回,退出循环,忽略该数字及以后的所有字符;
- 数字越界:因为 int 型的数据范围是 [-231,231-1],即
[-2,147,483,648, 2,147,483,647]
,所以我们定义了一个int最大值除以10的数字limit
,用低于 int 型数据长度一位的数据 res 与 limit 进行比较,判断是否越界,如果 res 与 limit 相等,那就再判断一下当前的字符 ch[i] 是否 > 7,如果大于 7,则说明此数字一定越界,根据符号位判断一下,返回最大/最小值即可。……
【实现策略】:
- 使用
trim()
方法去掉字符串的首尾空格,并将其转换成字符数组array
;- 根据字符数组 array 的长度判断是否属于空字符串
“ ”
,如果是的话,则返回0;- 定义
res
用来返回最终结果,定义sign
用来记录符号位,定义i
用来作为数字位的索引;- 定义
limit
用来在遍历中判断 res 是否越界,其值为Integer.MAX_VALUE / 10
;- 判断首位是否存在符号位,‘-’ 用 -1 表示,如果无符号位,则默认位正,并把 数据位索引
idx
移动到首位;- 循环遍历字符数组
array
,判断其中的字符是否为 非数字字符,如果是,则直接跳出循环,忽略之后所有字符;如果不是,则继续向下判断是否越界,每循环一次,res 就会 * 10 进位拼接一次,直到碰到非数字字符或数组终止时结束循环。
class Solution {
// Solution2: 自定义函数
// 需要注意的方面:
// 1. 空格 -- (trim()方法去除首尾空格)
// 2. 非 0~9 之间的字符 -- (如果当前字符不是0~9之间的数字,直接退出)
// 3. 数字越界 -- int型的数据范围:[-2,147,483,648, 2,147,483,647], 即[-2^31, 2^31 - 1]
// 4. 有符号 -- (设置变量 sign 作为标志位, 1:代表正, -1:代表负)
public int strToInt(String str) {
//去除str首尾的多余空格
char[] array = str.trim().toCharArray();
//如果array的长度为0 返回0
if (array.length==0) return 0;
//sign表示标志位 1为正 -1 为负 i代表array从何处开始遍历
int res = 0, sign = 1, i = 1;
//设置限制值,因为在遍历中先判断res是否越界,再向res赋值,因而对limit的要求/10
int limit = Integer.MAX_VALUE / 10;
//如果array[0]=- 表明该数是负数 sign =-1
if (array[0]=='-') sign = -1;
else if (array[0]!='+') i = 0; // 代表此时符号位不存在,默认为正,且数字位从数组下标0开始
for (int j = i; j < array.length; j++) {
//判断当前字符是否为数字 不是直接退出
if (array[j]>'9'||array[j]<'0') break;
//判断遍历到j-1的位置后 res是否大于limit 如果当前res已经大于limit 加上array[j]一定越界
//当res等于limit时,我们需要判断array[j]是否大于Integer.MAX_VALUE的末位数7
if (res>limit || res==limit && array[j]>'7') return sign == 1 ? Integer.MAX_VALUE : Integer.MIN_VALUE;
res = res * 10 + (array[j] - '0');
}
return res * sign;
}
}
3. 参考资料
[1] 面试题67. 把字符串转换成整数(数字越界处理,清晰图解)-- Krahets