本题最难点在于如何用Python模拟解决 Int32
溢出问题。
题目
请你来实现一个 atoi
函数,使其能将字符串转换成整数。 首先,该函数会根据需要丢弃无用的开头空格字符 ,直到寻找到第一个非空格的字符为止。 当我们寻找到的第一个非空字符为正或者负号时 ,则将该符号与之后面尽可能多的连续数字组合起来 ,作为该整数的正负号;假如第一个非空字符是数字,则直接将其与之后连续的数字字符组合起来,形成整数 。 该字符串除了有效的整数部分之后也可能会存在多余的字符,这些字符可以被忽略, 它们对于函数不应该造成影响。 注意:假如该字符串中的第一个非空格字符不是一个有效整数字符、字符串为空或字符串仅包含空白字符时,则你的函数不需要进行转换。 在任何情况下,若函数不能进行有效的转换时,请返回 0。
说明: 假设我们的环境只能存储 32 位大小的有符号整数,那么其数值范围为 [\(-2^{31}\) , \(2^{31}-1\) ]。 如果数值超过这个范围,请返回 INT_MAX (\(2^{31}-1\) ) 或 INT_MIN (\(-2^{31}\) )。
示例
示例 1:
示例 2:
解释: 第一个非空白字符为 '-', 它是一个负号。 我们尽可能将负号与后面所有连续出现的数字组合起来,最后得到 -42 。
示例 3:
输入: "4193 with words" 输出: 4193
解释: 转换截止于数字 '3' ,因为它的下一个字符不为数字。
示例 4:
解释: 第一个非空字符是 'w', 但它不是数字或正、负号。 因此无法执行有效的转换。
示例 5:
输入: "-91283472332 " 输出: -2147483648
解释: 数字 "-91283472332" 超过 32 位有符号整数范围。 因此返回 INT_MIN (\(−2^{31}\) ) 。
测试用例
功能测试(输入的字符串表示正数、负数和0)。 边界值测试(最大的正整数;最小的负整数)。 特殊输入测试(输入的字符串为 nullptr
指针;输入的字符串为空字符串;输入的字符串中有非数字字符等)。
考察知识点
字符串处理、正则项匹配、条件判定逻辑
核心思想
使用正则表达式解决该问题
r'^[\+\-]?\d+'
解释:
^:匹配字符串开头
[+-]:代表一个+字符或-字符
?:前面一个字符可有可无
一个数字
+:前面一个字符的一个或多个
:一个非数字字符
*:前面一个字符的0个或多个
为什么可以使用正则表达式?如果整数过大溢出怎么办?用该方法来防止结果越界
max (min (result, 2 **31 - 1 ), -2 **31 )
题目中描述: 假设我们的环境只能存储 32 位大小的有符号整数
首先,这个假设对于 Python 不成立,Python 不存在 32 位的 int 类型。其次,即使搜索到的字符串转32位整数可能导致溢出,我们也可以直接通过字符串判断是否存在溢出的情况(比如 try 函数 或 判断字符串长度 + 字符串比较)。
参考连接
通过字符串遍历来解决该问题
这道题也可以通过直接遍历字符串来处理,做好特殊情况处理即可。
本题难点 如何检测 非空格 第一个字符 检测到之后,如何避免重复检测
解决方案 设置变量:FirstCharCheck = True
if FirstCharCheck
遇到 空格‘ ’, continue
(直接跳过,不会改变flag的值)
遇到 + or -
,更新 sign
符号,并检测 i+1
位置是否为数字,决定是否提前 return
。+
的ASCII码是43 ,-
的ASCII码是45 所以 sign = 44-ord('+/-')
。
遇到 ‘数字’,ans = ans*10 + ord(str[i]) - ord('0')
令 FirstCharCheck = False
,循环到下次时 FirstCharCheck = False
,避免了重复检测。
else
每次遇到digital,添加到ans上 ans = ans*10 + ord(str[i]) - ord('0')
。
遇到非digital,跳出循环 。
最终返回时,对于溢出的数字要做越界判定:
return max (min (ans, (1 <<31 )-1 ), -(1 <<31 ))
实际上,正常来说不应该在最后返回时才做溢出判断,因为数字的大小压根儿就不能超过 [-2147483648, 2147483647] 这个范围。所以,应该在计算过程中做溢出判断。
tmp = (ord (item) - ord_0) * (10 **count)if (not flag and res < 147483648 and tmp <= 2000000000 ) or (flag and res < 147483647 and tmp <= 2000000000 ): pass else : is_valid = False
参考连接
Python版本
一个使用正则表达式的版本
简洁,但是完成难度大,可读性低,依赖正则模块。
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 import reclass Solution : def myAtoi (self, s: str ) -> int: return max ( min ( int (*re.findall( r'^[\+\-]?\d+' , s.lstrip()) ), 2 **31 - 1 ), -2 **31 )import reclass Solution : def myAtoi (self, str : str ) -> int: INT_MAX = 2147483647 INT_MIN = -2147483648 str = str .lstrip() num_re = re.compile (r'^[\+\-]?\d+' ) num = num_re.findall(str ) num = int (*num) return max (min (num,INT_MAX),INT_MIN)
遍历字符串 进行判断的方法
在最终返回时才做溢出判断(仅适用于Python所以不推荐)
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 class Solution : def strToInt (self, _str: str ) -> int: sign = 1 ans = 0 FirstCharCheck = True for i in range (len (_str)): if FirstCharCheck: if _str[i] == ' ' : continue elif _str[i] == '-' or _str[i] == '+' : sign = 44 -ord (_str[i]) if i+1 < len (_str) and not _str[i+1 ].isdigit(): return 0 elif _str[i].isdigit(): ans = ans*10 + (ord (_str[i])-ord ('0' )) else : return 0 FirstCharCheck = False elif _str[i].isdigit(): ans = ans * 10 + (ord (_str[i]) - ord ('0' )) else : break ans *= sign return max (min (ans, (1 <<31 )-1 ), -(1 <<31 ))
在计算过程中就实时进行溢出判断(适用于所有类型的语言,推荐!!! )
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 class Solution : def __init__ (self ): self.count = 0 self.ans = 0 def autoAdd (self, char, sign ): tmp = (ord (char) - ord ('0' )) if self.ans > 214748365 : return False elif self.ans == 214748364 and ((sign == -1 and tmp > 8 ) or (sign == 1 and tmp > 7 )): return False else : self.ans = self.ans*10 + tmp self.count += 1 return True def strToInt (self, _str: str ) -> int: sign = 1 FirstCharCheck = True for i in range (len (_str)): if FirstCharCheck: if _str[i] == ' ' : continue elif _str[i] == '-' or _str[i] == '+' : sign = 44 -ord (_str[i]) if i+1 < len (_str) and not _str[i+1 ].isdigit(): return 0 elif _str[i].isdigit(): if not self.autoAdd(_str[i], sign): return ((1 <<31 )-1 ) if sign == 1 else -(1 <<31 ) else : return 0 FirstCharCheck = False elif _str[i].isdigit(): if not self.autoAdd(_str[i], sign): return ((1 <<31 )-1 ) if sign == 1 else -(1 <<31 ) else : break self.ans *= sign return self.ans
剑指offer版本
LeetCode的题目相较于《剑指offer》的原题做了改动,这里放上符合《剑指offer》题目要求的代码。
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 def string2int32 (_str: str ) -> int: """convert str to int :param _str: str :return int: """ if not isinstance (_str, str ): raise ValueError if len (_str) == 0 : raise ValueError flag = True ord_0 = ord ('0' ) ord_9 = ord ('9' ) if _str[0 ] == '-' : _str = _str[1 :] flag = False for _char in _str: tmp = ord (_char) if tmp < ord_0 or tmp > ord_9: raise ValueError res = 0 count = 0 is_valid = True for item in _str[::-1 ]: is_valid = True tmp = (ord (item) - ord_0) * (10 **count) if (not flag and res < 147483648 and tmp <= 2000000000 ) or (flag and res < 147483647 and tmp <= 2000000000 ): pass else : is_valid = False if is_valid: res += tmp count += 1 else : raise ValueError return res if flag else (0 -res)
有效语法糖
如何约束返回值的范围
通过使用Python内建的max和min函数 可以用简洁的方法约束结果的范围
return max (min (result, MAX), MIN) if result<MAX: if result>MIN: return result else : return MIN else : return MAX
Python自带内建函数去除字符串开头、结尾的空格
>>> a = ' 112dgsd 232 sdfsaf 123666 ' >>> a.lstrip() '112dgsd 232 sdfsaf 123666 ' >>> a.rstrip() ' 112dgsd 232 sdfsaf 123666' >>> a.strip() '112dgsd 232 sdfsaf 123666'
Python解包骚操作
将可迭代对象分解出来的一种方法。参考连接
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 >>> a, b, *c = [1 ,2 ,3 ,4 ]>>> c [3 , 4 ]>>> num = ['123' ]>>> num = *num"SyntaxError: can't use starred expression here" >>> num = int (*num)>>> num123 >>> list1 = [1 ,2 ,3 ]>>> list2 = range (3 ,6 )>>> [*list1, *list2] [1 , 2 , 3 , 3 , 4 , 5 ]>>> def func (a, b, c ): print(a+b+c) >>> parameter = {'a' :1 , 'b' :2 , 'c' :3 }>>> func(**parameter) 6 >>> func(a=1 , b=2 , c=3 ) 6
不使用str()将单个int转成str
使用ord()函数,不调用int方法将单个数字char转化成int,将‘+’、‘-’转化成1、-1。ord() 函数 是 chr() 函数(对于8位的ASCII字符串)或 unichr() 函数(对于Unicode对象)的配对函数,它以一个字符(长度为1的字符串)作为参数,返回对应的 ASCII 数值,或者 Unicode 数值,如果所给的 Unicode 字符超出了你的 Python 定义范围,则会引发一个 TypeError 的异常。
>>> a = '6' >>> ord (a) - ord ('0' )6 >>> sgin = '+' >>> 44 -ord (sgin) 1 >>> sgin = '-' >>> 44 -ord (sgin) -1
使用内建函数判断字符是否是数字
>>> a = 'a' >>> a.isdigit()False >>> a = '1' >>> a.isdigit()True