← 返回题目列表

如何反转一个 32 位整数并处理溢出?(LeetCode 7)

高频 中等 第 12 / 27 题 更新于 2026/07/28
数学与数论整数反转溢出处理取模

简化版

把一个 32 位有符号整数的数字部分反转123 → 321-123 → -321120 → 21)。若反转后超出 int 范围 [-2³¹, 2³¹-1] 就返回 0。做法是不断 % 10 取末位、/ 10 去末位,把末位累加到结果 res = res * 10 + digit。核心难点是溢出判断:必须在 res * 10 + digit 之前判断会不会越界(因为溢出后就没法判了),拿 resInteger.MAX_VALUE / 10 比较。

详细版

int reverse(int x) {
    int res = 0;
    while (x != 0) {
        int digit = x % 10;        // 取末位(对负数,Java 的 % 保留符号,如 -123%10=-3)
        x /= 10;                   // 去末位
        // 溢出判断:累加前先判(正负边界都要考虑)
        if (res > Integer.MAX_VALUE / 10 || (res == Integer.MAX_VALUE / 10 && digit > 7))
            return 0;
        if (res < Integer.MIN_VALUE / 10 || (res == Integer.MIN_VALUE / 10 && digit < -8))
            return 0;
        res = res * 10 + digit;
    }
    return res;
}
  • 取末位累加res = res * 10 + (x % 10),逐位把 x 的末尾搬到 res 的高位。
  • 负数处理:Java 的 % 对负数保留符号(-123 % 10 = -3),所以负数无需特殊转换,累加天然带符号。
  • 溢出判断在累加前res 接近 MAX/10(214748364)时,再看下一位是否让它越过 2147483647 / -2147483648
  • 复杂度:O(log x)(位数)。

完整版教学

一、基本反转:取末位、搬高位

反转整数的核心操作循环三步:

  • 取末位digit = x % 10
  • 去末位x /= 10
  • 累加到结果res = res * 10 + digit——res 每次先 ×10 腾出个位,把 x 的当前末位接上去。

这样 x 的末位第一个被取出、经过多次 ×10 被推到 res 的最高位;x 的最高位最后取出、留在 res 的个位——恰好反转。120 反转:末位 0→res=0,2→res=2,1→res=21,自然去掉了前导零。

二、负数为什么不用特殊处理

Java(和 C++)的取模 % 对负数保留被除数的符号-123 % 10 = -3(不是 7)。所以对 -123

  • -123 % 10 = -3res = -3
  • -12 % 10 = -2res = -32
  • -1 % 10 = -1res = -321

结果 -321 自带负号,不需要先取绝对值再补符号。这是 Java/C++ 取模符号规则带来的便利(Python 的 % 结果符号跟除数,需注意差异)。

三、溢出判断:必须在累加前(核心难点)

反转后的值可能超出 int 范围(如 1534236469 反转成 9646324351 超过 2³¹-1)。而 res = res * 10 + digit 一旦溢出,res 就变成错误值,事后无法判断。所以必须在乘加之前预判

  • 正溢出Integer.MAX_VALUE = 2147483647MAX/10 = 214748364。若 res > 214748364,则 res*10 必超;若 res == 214748364digit > 7(MAX 末位是 7),也超。→ 返回 0。
  • 负溢出Integer.MIN_VALUE = -2147483648MIN/10 = -214748364。若 res < -214748364,或 res == -214748364digit < -8(MIN 末位是 -8),也越界。→ 返回 0。

这个「和 MAX/10、MIN/10 比较,边界再看末位」的技巧是所有「整数累加防溢出」题的通用模板(atoi 也是)。

四、用 long 简化(若允许)

如果语言/题目允许用更大的类型,可以long 累加,最后判断是否超出 int 范围:

long res = 0;
while (x != 0) { res = res * 10 + x % 10; x /= 10; }
return (res < Integer.MIN_VALUE || res > Integer.MAX_VALUE) ? 0 : (int) res;

简单很多。但本题经典要求「只用 32 位」(不借助 long),那就得用上一节的「提前判断」写法。面试常追问「不用 long 怎么做」,所以两种都要会。

五、易错点

  • 事后判溢出:最经典错误。溢出后 res 已错,判断无意义,必须提前判
  • 忘记负溢出边界:MAX 末位 7、MIN 末位 8(绝对值),两个边界的末位不同,别只判正的。
  • 前导零:反转 1001res 累加天然去零),不用特殊处理。
  • Integer.MIN_VALUE 本身-2147483648 取绝对值会溢出,所以别用「先取绝对值」的思路,直接用带符号的 % 累加最稳。

六、在临界值上验证预判公式

预判把不等式 MIN <= res*10+digit <= MAX 改写为对 res 和最后一位的比较,避免危险乘法先发生。正边界前缀是 214748364,只有 digit≤7 安全;负边界前缀是 -214748364,只有 digit≥-8 安全。

x=1463847412 反转目标 2147483641
到最后一步前 res=214748364, digit=1
res==MAX/10 且 1<=7 -> 安全
若 digit=8,则 2147483648>MAX -> 返回 0
负侧 res=-214748364, digit=-8 仍等于 MIN
digit=-9 则小于 MIN -> 返回 0
判断必须发生在乘 10 之前
校验维度本题必须保持的结论
循环/递推不变量每轮累加前 res 在 int 范围内;通过预判后本轮乘加仍在 int 范围内。
边界条件Integer.MIN_VALUE 不能取绝对值;直接使用带符号 %10 可统一处理正负。
复杂度与代价每轮去掉一个十进制位,O(log
自检方式用最小输入、典型输入和触及边界的输入分别手推,结果应与定义一致。

这组推演的作用不是替代证明,而是把抽象规则落到每一步状态变化上。面试写完代码后,应主动指出不变量如何保证不漏解,以及边界为何不会越界或死循环。若手推结果与定义不一致,应先修正状态语义,而不是继续给代码打补丁。

七、面试现场如何验证这道题

数学题不能只凭样例相信公式,必须同时核对定义域、推导条件和定宽整数边界。本题应先复述这条不变量:每轮累加前 res 在 int 范围内;通过预判后本轮乘加仍在 int 范围内。只有代码的初始化、更新顺序和终止条件都维持它,样例通过才具有证明力。

  • 典型路径从“x=1463847412 反转目标 2147483641”开始手推,最后应得到“判断必须发生在乘 10 之前”。
  • 边界复核:Integer.MIN_VALUE 不能取绝对值;直接使用带符号 %10 可统一处理正负。
  • 代价复核:每轮去掉一个十进制位,O(log |x|) 时间 O(1) 空间。
  • 用 0、1、最小合法值和最大合法值检查公式的定义域。
  • 乘法、取绝对值或取负前先判断是否可能触及 Integer.MIN_VALUE 等不对称边界。
  • 若算法依赖单调性、抵消或整除关系,要明确题设保证何时成立、何时必须额外验证。

最后要主动构造一个“少写一次循环、改变一个不等号或交换两行更新就会失败”的反例。能说清反例破坏了哪条不变量,比只报出复杂度更能证明真正掌握了算法。

记忆钩子:本题的代码可以压缩,但“每轮累加前 res 在 int 范围内;通过预判后本轮乘加仍在 int 范围内。”这条正确性主线不能省。

八、常见误区与追问

  • 误区:溢出后判断 res 是否越界仍有效。 int 已环绕成另一个值,原始越界信息丢失,必须事前判断。
  • 误区:正负边界的末位阈值相同。 MAX 末位是 7,MIN 的绝对值末位是 8,范围不对称。
  • 误区:先对 x 取绝对值最省事。 abs(Integer.MIN_VALUE) 仍溢出,带符号逐位处理更安全。
  • 追问:为什么 Java 负数取模能直接使用? Java 余数与被除数同号,所以 -123%10=-3,累加自然保持负号。
  • 追问:120 为什么返回 21? 首先取出的 0 不改变 res,反转后的前导零在整数表示中自然消失。
  • 追问:若允许 long 是否仍需预判? 可先用 long 累加再比较 int 范围,但面试常追问不借助更宽类型的写法。

九、加强记忆

整数反转 = res = res * 10 + x % 10,逐位搬末位到高位。Java/C++ 的 % 对负数保留符号-123%10=-3),所以负数无需特殊处理、结果自带负号。核心难点溢出必须在累加前预判res > MAX/10(214748364)或 == 且 digit>7 → 正溢出;res < MIN/10== 且 digit<-8 → 负溢出,都返回 0。别事后判、别对 MIN_VALUE 取绝对值。若允许用 long 则累加后判范围更简单。核心:边搬边判,MAX/10 是警戒线