8. 字符串转换整数 (atoi) - 力扣(LeetCode)
一、题目
请你来实现一个 myAtoi(string s) 函数,使其能将字符串转换成一个 32 位有符号整数(类似 C/C++ 中的 atoi 函数)。
函数 myAtoi(string s) 的算法如下:
- 读入字符串并丢弃无用的前导空格
- 检查下一个字符(假设还未到字符末尾)为正还是负号,读取该字符(如果有)。 确定最终结果是负数还是正数。 如果两者都不存在,则假定结果为正。
- 读入下一个字符,直到到达下一个非数字字符或到达输入的结尾。字符串的其余部分将被忽略。
- 将前面步骤读入的这些数字转换为整数(即,"123" -> 123, "0032" -> 32)。如果没有读入数字,则整数为 0 。必要时更改符号(从步骤 2 开始)。
- 如果整数数超过 32 位有符号整数范围 [−231, 231 − 1] ,需要截断这个整数,使其保持在这个范围内。具体来说,小于 −231 的整数应该被固定为 −231 ,大于 231 − 1 的整数应该被固定为 231 − 1 。
- 返回整数作为最终结果。
注意:
- 本题中的空白字符只包括空格字符 ' ' 。
- 除前导空格或数字后的其余字符串外,请勿忽略 任何其他字符。
示例 1:
输入:s = "42"
输出:42
解释:加粗的字符串为已经读入的字符,插入符号是当前读取的字符。
第 1 步:"42"(当前没有读入字符,因为没有前导空格)
^
第 2 步:"42"(当前没有读入字符,因为这里不存在 '-' 或者 '+')
^
第 3 步:"42"(读入 "42")
^
解析得到整数 42 。
由于 "42" 在范围 [-231, 231 - 1] 内,最终结果为 42 。
示例 2:
输入:s = " -42"
输出:-42
解释:
第 1 步:" -42"(读入前导空格,但忽视掉)
^
第 2 步:" -42"(读入 '-' 字符,所以结果应该是负数)
^
第 3 步:" -42"(读入 "42")
^
解析得到整数 -42 。
由于 "-42" 在范围 [-231, 231 - 1] 内,最终结果为 -42 。
示例 3:
输入:s = "4193 with words"
输出:4193
解释:
第 1 步:"4193 with words"(当前没有读入字符,因为没有前导空格)
^
第 2 步:"4193 with words"(当前没有读入字符,因为这里不存在 '-' 或者 '+')
^
第 3 步:"4193 with words"(读入 "4193";由于下一个字符不是一个数字,所以读入停止)
^
解析得到整数 4193 。
由于 "4193" 在范围 [-231, 231 - 1] 内,最终结果为 4193 。
提示:
0 <= s.length <= 200
-
s
由英文字母(大写和小写)、数字(0-9
)、' '
、'+'
、'-'
和'.'
组成
二、代码
class Solution {
public int myAtoi(String str) {
// 字符串转换为字符数组
char[] s = str.toCharArray();
// 字符串长度
int n = s.length;
// 标记是否已经完成了去除前缀空格的流程
boolean flag1 = false;
// 标记是否已经完成了读取符号位的流程
boolean flag2 = false;
// 记录符号位
int signed = 1;
// 记录答案
// 这种字符串转数字类型的题,需要在过程中用负数统计,因为系统最小值绝对值比最大值大一个(负数的范围比正数的范围多1个),拿它做底更安全。
// 比如转换字符串"-2147483648"为一个int数,用正数处理就不够了,只能用负数处理才行。
// 如果不是负数的,也先将其转化为负数,处理完了之后再转回正数。
int ans = 0;
// 下面就是计算两个不能越界的范围,因为在统计过程中我们使用的是负数,所以这里就用负数的最小值来判断溢出边界
int minq = Integer.MIN_VALUE / 10;
int minr = Integer.MIN_VALUE % 10;
// 开始遍历字符串
for (int i = 0; i < n; i++) {
// 如果去除前缀空格的流程,并且当前的字符仍然是空格,则跳过该字符继续下一个
if (!flag1 && s[i] == ' ') {
} else {
// 当第一次找到了一个不是空格的字符时,说明第一个阶段已经结束了,将flag设置为true
flag1 = true;
// 第一次进入到该分支,flag2一定是false,我们要先去判断第一个非空格的字符是否是符号位
if (!flag2) {
// 先将flag2设置为true,因为该流程只会执行一次
flag2 = true;
// 如果符号位是负,则将signed设置为-1
if (s[i] == '-') {
signed = -1;
// 该位置是非数字,直接跳过
continue;
} else if (s[i] == '+') {
// 如果有正号符号位,也需要跳过后续的流程
continue;
}
}
// 当完成了第一个流程和第二个流程后,后面就开始读取数字字符了,当第一次读取到非数字字符时,就结束直接跳出循环,后面的字符串就不管了
if (s[i] >= '0' && s[i] <= '9') {
// 【这里有一个技巧点,上面说了我们要按照负数去做处理,这样更安全,所以这里要用'0' - s[i],当所有流程结束后我们再将符号位转回去即可】
int num = '0' - s[i];
// 【第二个技巧——防溢出】
// 因为我们使用负数来收集的,所以循环中的ans一定是一个负数
// 1) ans结果是负数,如果比系统最小/10还小,那么在后面的乘10操作之后必溢出,这种情况就可以直接返回系统最大或系统最小值
// 2) 如果ans == 系统最小值,则乘10之后不会溢出,此时res*10=-2147483640,相当于系统最小值把最后一位变成0,也就是说只要是在个位上加一个小于-8的数(即小于系统最小值个位的那个数minr),就一定低于系统最小值,溢出了。在后面的操作中,ans要加num,所以如果num小于minr,则加上这部分必溢出。这种情况就可以直接返回系统最大或系统最小值
if (ans < minq || (ans == minq && num < minr)) {
// 根据符号位来决定返回最大值还是最小值
return signed == -1 ? Integer.MIN_VALUE : Integer.MAX_VALUE;
}
// 收集数字
ans = ans * 10 + num;
} else {
// 在收集数字的过程中第一次遍历到非数字字符时就结束循环
break;
}
}
}
// 如果此时ans此时正好是系统最小值,但是符号位是正数,我们知道最大值的绝对值比最小值的绝对值小1,所以此时答案应该是已经超过系统最大值了,算溢出了,直接返回系统最大
if (signed == 1 && ans == Integer.MIN_VALUE) {
return Integer.MAX_VALUE;
}
// 因为之前我们是按照负数来收集,执行到这里就说明最终的答案没有溢出(大于等于系统最小,小于等于系统最大),所以我们直接将ans取相反数,消除之前按负数收集的影响
ans = -ans;
// 返回答案 (符号位 * ans)
return (signed * ans);
}
}
三、解题思路
这道题不难,就是麻烦,就是需要考虑很多判断条件。并且这道题最关键的一个技巧就是判断大数是否溢出的技巧。文章来源:https://www.toymoban.com/news/detail-470413.html
详细的讲解都在代码注释中文章来源地址https://www.toymoban.com/news/detail-470413.html
到了这里,关于【LeetCode】字符串转换整数 (atoi) [M](模拟)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!