8. String to Integer (atoi)
On LeetCode ->Reformulated question¶
Parse a string into a 32-bit signed integer.
- Ignore leading spaces.
- Read one optional
+or-. - Read consecutive digits only.
- If no digit is read, return
0. - Clamp result to \([-2^{31}, 2^{31} - 1]\).
Example:
" -042abc" -> -42- skip spaces
- read
- - read
042 - stop at
a
Key trick¶
Build the number left to right and check overflow before doing:
This avoids converting a huge substring first and handles clamping cleanly.
Trap¶
- Accepting sign after digits, like
"0-1". - Accepting multiple signs, like
"-+12". - Forgetting that no digits after sign means
0, like"+". - Using
strip()can hide that only leading spaces should be ignored. - Overflow check differs for positive and negative bounds.
Why is this question interesting?¶
It is a small parser.
It tests careful state handling, edge cases, and overflow logic more than Python syntax.
Solve the problem with idiomatic python¶
class Solution:
def myAtoi(self, s: str) -> int:
INT_MIN = -(2 ** 31)
INT_MAX = 2 ** 31 - 1
i = 0
n = len(s)
# Skip leading spaces only.
while i < n and s[i] == " ":
i += 1
# Read optional sign.
sign = 1
if i < n and s[i] in "+-":
if s[i] == "-":
sign = -1
i += 1
# Read digits and clamp on the fly.
value = 0
while i < n and s[i].isdigit():
digit = ord(s[i]) - ord("0")
if sign == 1 and value > (INT_MAX - digit) // 10:
return INT_MAX
if sign == -1 and value > ((-INT_MIN) - digit) // 10:
return INT_MIN
value = value * 10 + digit
i += 1
return sign * value
Pytest test¶
Use one parametrized test covering normal parse, stop-on-nondigit, invalid start, sign handling, and clamping.
import pytest
@pytest.mark.parametrize(
("s", "expected"),
[
("42", 42),
(" -042", -42),
("1337c0d3", 1337),
("0-1", 0),
("words and 987", 0),
("+", 0),
("-+12", 0),
(" +0 123", 0),
("2147483647", 2147483647),
("2147483648", 2147483647),
("-2147483648", -2147483648),
("-2147483649", -2147483648),
("", 0),
],
)
def test_my_atoi(s, expected):
assert Solution().myAtoi(s) == expected
Comment my solution¶
Good:
- Correct overall parsing order.
- Overflow is checked before updating the number.
- Tests cover many important cases.
Can be improved:
digits = []stores all digits first, but you can parse and clamp in one pass.isspace()is broader than needed; the problem only mentions space, though it is usually still accepted.print("foo")andprint("bar")should not be there.int(d)is done twice in the loop.- A few useful tests are missing, like
"","+"," +0 123", and"2147483647".
import pytest
class Solution:
def myAtoi(self, s: str) -> int:
i = 0
sign = 1
# skip whitespaces
while i < len(s) and s[i].isspace():
i += 1
# sign
if i < len(s):
if s[i] == "-":
sign = -1
i +=1
elif s[i] == "+":
sign = 1
i +=1
# read up to the first non-digit
digits = []
while i < len(s) and s[i].isdigit():
digits.append(s[i])
i += 1
n = 0
for d in digits:
d = int(d)
# check if n*10 + digits.pop() is signed 32-bit
# if not we round it.
if sign == -1 and (n > (2**31 - d) // 10):
print("foo")
return - 2**31
if sign == 1 and (n > (2**31 - 1 - d) // 10):
print("bar")
return 2**31 - 1
n = n*10 + int(d)
return sign*n
@pytest.mark.parametrize(
("s", "expected"),
[
("42", 42),
(" 42", 42),
("-042", -42),
("+1", 1),
("1337c0d3", 1337),
("0-1", 0),
("-2147483649", -2**31), # -(2**31 + 1) # -2147483649
("-1099511627776", -2**31), # -(2**40) # -2147483648
("2147483648", 2**31 - 1), # 2**31 # 2147483648
("1099511627776", 2**31 - 1), # 2**40 # 2147483648
# not correct input, return 0
("words and 987", 0),
("-+12", 0)
]
)
def test_myAtoi(s, expected):
assert Solution().myAtoi(s) == expected
# ord() is used in AI generated solution
#
# ord("a") # 97
# ord("b") # 98
# ord("c") # 99
# ord("0") # 48
# ord("1") # 49
# ord("2") # 50
# ord("1") - ord("0") # 1
# ord("2") - ord("0") # 2