Skip to content

4.30.7 — Reverse Integer

LeetCode 7 · Medium

The problem

Reverse the digits of a signed 32-bit integer. If the reversed value falls outside the 32-bit signed range, return 0. You may not use a 64-bit type to help.

123    →  321
-123   →  -321
120    →  21      (the trailing zero disappears)
1534236469  →  0  (reversed, it overflows)

The range is -2^{31} to 2^{31}-1, that is -2{,}147{,}483{,}648 to 2{,}147{,}483{,}647.

The pattern

Reversing itself is the digit loop from 4.28.4:

python
while x:
    x, digit = divmod(x, 10)
    result = result * 10 + digit

Trailing zeros vanish for free, because a leading 0 in the result contributes nothing.

The entire difficulty is the overflow check, and the constraint is what makes it interesting: you cannot compute the value and then check, because in a fixed-width language the computation has already wrapped by then.

Check before you multiply, not after.

Deriving the check

The dangerous line is result = result * 10 + digit. It overflows when the new value would exceed INT_MAX = 2147483647.

Rearrange to test result instead of the result. Overflow happens when:

\text{result} > \frac{\text{INT\_MAX}}{10} \quad\text{or}\quad \left(\text{result} = \frac{\text{INT\_MAX}}{10} \ \text{and}\ \text{digit} > 7\right)

INT_MAX // 10 is 214,748,364, and the last digit of INT_MAX is 7.

Read it in words: if the accumulated value is already bigger than a tenth of the limit, multiplying by 10 will exceed it. If it is exactly a tenth of the limit, only a final digit above 7 pushes it over.

The negative side mirrors it, with INT_MIN // 10 = -214748364 and a last digit of 8 — because INT_MIN is one further from zero than INT_MAX, an asymmetry of two's complement worth knowing.

In practice the boundary-digit cases cannot occur, because the input itself must have been a valid 32-bit integer, so its reversal cannot land exactly on the boundary with an extra digit. Write the check anyway — the interviewer is testing whether you thought about it, and reasoning "it cannot happen" without the check is weaker than including it.

The solution

python
class Solution:
    def reverse(self, x: int) -> int:
        INT_MAX, INT_MIN = 2**31 - 1, -2**31

        sign = -1 if x < 0 else 1
        x = abs(x)
        result = 0

        while x:
            x, digit = divmod(x, 10)

            # check BEFORE the multiply
            if result > INT_MAX // 10 or (result == INT_MAX // 10 and digit > 7):
                return 0

            result = result * 10 + digit

        result *= sign
        return result if INT_MIN <= result <= INT_MAX else 0
ts
function reverse(x: number): number {
  const INT_MAX = 2 ** 31 - 1, INT_MIN = -(2 ** 31);

  const sign = x < 0 ? -1 : 1;
  x = Math.abs(x);
  let result = 0;

  while (x > 0) {
    const digit = x % 10;
    x = Math.floor(x / 10);

    if (result > Math.floor(INT_MAX / 10)
        || (result === Math.floor(INT_MAX / 10) && digit > 7)) {
      return 0;
    }

    result = result * 10 + digit;
  }

  result *= sign;
  return result >= INT_MIN && result <= INT_MAX ? result : 0;
}

Taking the sign out first removes every question about how negative division and modulo behave, which differ between languages. Python's divmod(-13, 10) gives (-2, 7), not (-1, -3) — the floor-versus-truncate issue from 4.8.3. Working with the absolute value sidesteps it entirely.

abs(INT_MIN) would itself overflow in a fixed-width language, since 2^{31} does not fit. In Python it is fine. In Java you would handle the sign differently, or widen to long. Worth naming.

The final range check is belt and braces in Python, where the loop's check has already done the work, but it makes the intent explicit.

Complexity

O(\log x) time — the number of digits. O(1) space.

Why Python makes this problem strange

Python integers are unbounded, so nothing overflows and the check is pure simulation of a constraint the language does not have. That can make the problem feel artificial.

It is not. In Java, C, C++, Go and Rust the overflow is real, silent, and the source of genuine bugs — a wrapped value looks like a plausible number and propagates. Signed integer overflow is undefined behaviour in C and C++, which means the compiler may assume it never happens and optimise accordingly, producing behaviour that surprises everyone.

Say this if asked why the check matters. It reframes an artificial-feeling exercise as a defensive habit.

Where this goes next

  • String to Integer (atoi) (LeetCode 8) — the same overflow check plus whitespace, sign and non-digit handling. A notoriously fiddly problem for exactly that reason.
  • Divide Two Integers (LeetCode 29) — the famous case is INT_MIN / -1, whose true answer is one past INT_MAX.
  • Palindrome Number — reverse half the number instead of all of it, which avoids overflow entirely. A neat demonstration that the best fix for overflow is often to not create the large value at all.
  • Chapter 1.3 covers two's complement and why INT_MIN has no positive counterpart.

What the interviewer will push on

"How do you detect overflow without a wider type?" Check result against INT_MAX // 10 before multiplying. Derive it rather than reciting it.

"Why 7 and 8?" The last digits of INT_MAX and INT_MIN.

"Why is INT_MIN different?" Two's complement has one more negative value than positive, so abs(INT_MIN) does not fit.

"How do you handle the sign?" Strip it first, so negative division behaviour never arises.

"Does this matter in Python?" No — and say why it matters everywhere else, including undefined behaviour in C.

One thing to volunteer: derive the boundary check out loud instead of quoting it. That derivation is the whole problem, and it is the difference between a memorised solution and a considered one.

Next: 4.31 is the last chapter of Part 4 — KMP, Z, rolling hashes and Manacher, the string algorithms that sit one level above everything the NeetCode 150 asks for.