模拟字符串转整数函数atoi
题目链接:8. 字符串转换整数 (atoi) - 力扣(LeetCode)
题目描述:
请你来实现一个 myAtoi(string s)
函数,使其能将字符串转换成一个 32 位有符号整数。
函数 myAtoi(string s)
的算法如下:
- 空格:读入字符串并丢弃无用的前导空格(
" "
) - 符号:检查下一个字符(假设还未到字符末尾)为
'-'
还是'+'
。如果两者都不存在,则假定结果为正。 - 转换:通过跳过前置零来读取该整数,直到遇到非数字字符或到达字符串的结尾。如果没有读取数字,则结果为0。
- 舍入:如果整数数超过 32 位有符号整数范围
[−2^31, 2^31 − 1]
,需要截断这个整数,使其保持在这个范围内。具体来说,小于−2^31
的整数应该被舍入为−2^31
,大于2^31 − 1
的整数应该被舍入为2^31 − 1
。
返回整数作为最终结果。
示例 1:
输入:s = "42"
输出:42
解释:加粗的字符串为已经读入的字符,插入符号是当前读取的字符。
带下划线线的字符是所读的内容,插入符号是当前读入位置。
第 1 步:"42"(当前没有读入字符,因为没有前导空格)
第 2 步:"42"(当前没有读入字符,因为这里不存在 '-' 或者 '+')
第 3 步:"42"(读入 "42")
示例 2:
输入:s = " -042"
输出:-42
解释:
第 1 步:"-042"(读入前导空格,但忽视掉)
第 2 步:" -042"(读入 '-' 字符,所以结果应该是负数)
第 3 步:" -042"(读入 "042",在结果中忽略前导零)
示例 3:
输入:s = "1337c0d3"
输出:1337
解释:
第 1 步:"1337c0d3"(当前没有读入字符,因为没有前导空格)
第 2 步:"1337c0d3"(当前没有读入字符,因为这里不存在 '-' 或者 '+')
第 3 步:"1337c0d3"(读入 "1337";由于下一个字符不是一个数字,所以读入停止)
示例 4:
输入:s = "0-1"
输出:0
解释:
第 1 步:"0-1" (当前没有读入字符,因为没有前导空格)
第 2 步:"0-1" (当前没有读入字符,因为这里不存在 '-' 或者 '+')
第 3 步:"0-1" (读入 "0";由于下一个字符不是一个数字,所以读入停止)
示例 5:
输入:s = "words and 987"
输出:0
解释:
读取在第一个非数字字符"w"
处停止。
提示:
0 <= s.length <= 200
s
由英文字母(大写和小写)、数字(0-9
)、' '
、'+'
、'-'
和'.'
组成
思路解析:
基本思路:
- 开始遇到非数字字符时返回0
- 否则继续
- 跳过空格
" "
- 遇到
+
或-
时处理返回值正负
- 跳过空格
📌
注意+
也要处理,+1
和1
虽然返回结果相同,但是如果直接输入的是+1
则此时下标指向+
而非数字1,从而导致最后的结果错误
-
- 处理数值返回
📌
注意处理数值时需要考虑每一次的计算结果是否超过32位整型的最大值和最小值
细节处理:
- 在获取字符串的数值时,有两种方法,第一种方法是将每一次获取的数值存入一个
vector<int>
中,最后根据位权求和,例如42
可以表示为4 * (int)pow(10, vector<int>().size() - 1 - 0) + 2 * (int)pow(10, vector<int>().size() - 1 - 1)
,但是这种方法如果存在前导0,那么也会放入vector<int>
中,从而导致需要额外处理前导0,所以需要考虑使用第二种方法:考虑到每一次都是从高位开始读取,则可以使用ret = ret * 10 + digit
(其中ret
为返回值,初始化为0,digit
为每一次获取的数字),在遍历字符数组时,例如542,高位优先读取,所以先读到5,此时,digit
为5,ret * 10
为0,所以此时ret
为5,接着遇到字符4,此时digit
为4,ret*10
为50,故ret
结果为54,最后遇到2,digit
为2,ret*10
为540,故结果为542。这个方法适合正向遍历,反向遍历还是需要用到vector
,那么如果存在前导0,这个方法则不需要考虑,因为当前导字符为0时,digit
为0,ret
也为0,ret*10+digit
就会为0,即ret
为0,如此往复直到遇到非前导0 - 越界问题:字符不存在越界,但是
int
类型存在,在32位系统下,范围为[INT_MIN, INT_MAX]
,因为最后的结果存储在ret
中,所以每次计算前判断ret
是否大于INT_MAX
或者小于INT_MIN,因为越界只会存在于ret = ret*10+digit
中,如果结果ret
此时越界了,那么计算*10+digit
前可能没有越界,所以考虑计算式(INT_MAX - digit) / 10
的结果为临界值,如果ret
大于该式,那么ret = ret*10+digit
肯定越界
参考代码:
/*
* @lc app=leetcode.cn id=8 lang=cpp
*
* [8] 字符串转换整数 (atoi)
*/
// @lc code=start
class Solution
{
public:
int myAtoi(string s)
{
int ret = 0;
// 处理开始为非数字字符
if (islower(s[0]) || isupper(s[0]))
{
return 0;
}
int flag = 1;
int i = 0;
// 处理空格
while (i < s.size() && isspace(s[i]))
{
i++;
}
// 处理符号,可能出现+1等情况需要考虑+
if (i < s.size() && (s[i] == '-' || s[i] == '+'))
{
// 处理负号情况
if (s[i] == '-')
{
flag = -1;
}
i++;
}
while (i < s.size() && isdigit(s[i]))
{
int digit = s[i] - '0';
// 判断是否越界
if (ret > (INT_MAX - digit) / 10)
{
// 检查(ret - digit) / 10是否越界,如果该式越界,则下面的算式也会越界
// 根据flag来判断返回正越界还是负越界
return flag == 1 ? INT_MAX : INT_MIN;
}
// 通过使用加法运算去除出现前导0的情况
ret = ret * 10 + digit;
i++;
}
return ret * flag;
}
};
// @lc code=end