资讯动态

整数翻转:LeetCode第7题边界条件与溢出判断实战解析

发布时间:2026/9/17 3:06:05 来源:尧图企业网站定制
LeetCode第7题“整数翻转”我愿称之为算法路上的“入门劝退题”。说它简单吧三分钟就能写出一版能跑的代码说它难吧第一次提交就通过的人真不多大家基本都会在边界条件上栽一次跟头。这道题考察的知识点非常集中——整数的拆位与重组、32位有符号整数的溢出判断、以及不同编程语言对负数取模的特性差异把这三个点吃透你不仅在LeetCode上多了一道AC题更重要的是把“用数学运算处理数字”和“处理边界条件的思维”这两项基本功练扎实了。这篇文章我会从题目拆解开始带你走一遍数组翻转中最经典的“数学拆位”思路再对比字符串方案和溢出判断的多种写法最后把C、Python、JavaScript里的典型大坑一个个排掉。无论你是刚开始刷题的新手还是准备面试想快速复盘的老兵这篇都能给你一些值得沉淀的东西。1. 题目拆解与核心考点1.1 题目描述到底在说什么原题要求非常简单给定一个32位有符号整数x返回将x中数字部分反转后的结果。如果反转后的整数超过32位有符号整数的范围[-2^31, 2^31 - 1]就返回0。举个例子输入123输出321输入-123输出-321输入120输出21输入1534236469输出0因为反转后是9646324351超出了32位范围注意题目里“32位有符号整数”这个限定。INT_MAX 2147483647INT_MIN -2147483648。这个范围直接决定了翻转后哪些值合法、哪些值必须被舍弃是整个题目的命门所在。很多初学者第一眼觉得这题就是“取余数拼接”但真正动手写时才意识到当数字末尾有0时拼接结果前面的0要不要去掉负数取余数会不会出问题翻转后超过范围怎么办这些细节恰恰是这道题被归类为“中等难度”的原因。1.2 表面上考“翻转”实际上在考什么如果只是考“翻转数字”这个动作直接把数字转成字符串再逆序输出就行。但题目设置了两道坎第一你不能无脑用64位整数暂存许多版本的题目环境默认不允许所以必须在累加过程中随时判断溢出第二数字可能是负数取模和除法的行为在不同语言下不一致。所以这道题真正考察的是三个能力数字拆解与重组用%取末位、用/去掉末位再把末位按10的幂累加回去。这是所有数字处理题的基本功。边界条件的敏感度写代码之前就要先问自己“如果输入恰好是-2147483648怎么办”这一个值的反转结果是-8463847412绝对值远大于INT_MAX必须返回0。但如果你用long存中间结果再和INT_MAX比较在大多数LeetCode语言环境里也能过只是失去了这道题最想训练你的“提前判断溢出”的能力。语言特性的理解C/C中正整数取模和整除是向零截断负数的行为同样向零截断Python中%的结果是非负余数//是向下取整。这些差异直接影响你写出的代码是否正确。说白了这道题不是让你背“标准答案”而是让你彻底搞清楚整数运算在计算底层是怎么工作的。搞明白这一点后面你做“回文数”“字符串转整数”这些题时会轻松很多。2. 整数翻转的三种解法与选型2.1 字符串翻转最直觉但不推荐的方案最直观的思路把整数转成字符串 → 判断正负号 → 翻转数字部分 → 再转回整数。用Python写出来大概是这样def reverse(x: int) - int: sign 1 if x 0: sign -1 x -x rev int(str(x)[::-1]) * sign if rev -2**31 or rev 2**31 - 1: return 0 return rev这段代码确实能通过而且可读性不错。它的好处是“省脑子”翻转逻辑交给语言内置的切片或reverse()方法你只需要处理符号和溢出判断。坏处也明显依赖语言API面试时容易让面试官觉得你“只会调库”。字符串和整数之间的转换有额外开销虽然这道题数据量小看不出来但对于刷题训练来说没有触及核心的数学思维。如果你的目标是训练算法思维用字符串方案永远是“隔靴搔痒”。我的建议是刚接触这道题时可以用字符串方案先AC一遍给自己建立信心然后立刻用数学方法再写一遍。刷题的价值在“思考过程”不在那一个绿色勾。2.2 数学拆位法面试官真正想看到的数学拆位法的核心只有三步令rev 0。每次取出x的末位数字pop x % 10然后让x x / 10去掉末位。把pop追加到rev的末尾rev rev * 10 pop。重复直到x 0。举个例子x 123第一次循环pop 3x 12rev 0 * 10 3 3第二次循环pop 2x 1rev 3 * 10 2 32第三次循环pop 1x 0rev 32 * 10 1 321整个过程就是“从低位往高位重建数字”完全不需要字符串的参与也天然处理了末尾为0的情况——120翻转时第一次pop 0rev还是0第二次pop 2后rev 2第三次pop 1后rev 21结果正确。这个方案的优雅之处在于它把一个“看起来需要字符串操作”的问题还原成了纯数学问题并且能在过程中随时检查溢出。这也是面试官最想看到的状态——你理解每一步在做什么而不是背模板。2.3 解法对比与选择建议方案时间复杂度空间复杂度可读性面试表现适用场景字符串翻转O(n)O(n)高一般快速AC、脑筋急转弯数学拆位O(n)O(1)中高好面试、基本功训练数学拆位提前溢出判断O(n)O(1)中很好目标环境不允许64位时n是数字的位数32位整数最多10位所以时间空间复杂度已经是极限优化。选型上我建议你务必掌握数学拆位法字符串方案了解一下思路就行因为面试时一旦被追问“为什么不用字符串”你说出的理由越深入加分越多。3. 手把手实现C语言解法逐步拆解3.1 取末位%运算的底层逻辑要写对数学拆位法首先得弄清%和/在C语言中遇到负数时的行为。C语言规定整数除法向零截断取余运算满足a (a / b) * b a % b。看两个例子-123 % 10 -3-123 / 10 -12-2147483648 % 10 -8-2147483648 / 10 -214748364你会发现负数取余得到的是负数而且除法的结果是向零截断的而不是向下取整。这意味着“末位数字”会带符号但没关系因为我们累加回去时也带着符号整个过程对负数同样成立。拿x -123试一下pop -3x -12rev -3pop -2x -1rev -32pop -1x 0rev -321结果完全正确。这就是C语言方案能把负数“无差别处理”的原因。3.2 溢出判断的具体推导算法本身很简单难点在于怎么在rev还没真正溢出之前就提前发现“下一步会溢出”。假设当前累积结果是rev下一次要拼接的末位数字是pop那么新值就是rev * 10 pop。我们要保证它在INT_MIN和INT_MAX之间。先看正方向。令INT_MAX 2147483647也就是214748364乘以10再加7。分两种情况讨论如果rev INT_MAX / 10也就是rev 214748364那么rev * 10至少是2147483650已经超过INT_MAX不管pop是多少结果一定溢出。如果rev INT_MAX / 10也就是rev 214748364那就要看pop是否大于7。大于7时rev * 10 pop 2147483647溢出等于7时刚好是最大值合法小于7时安全。所以正方向的判断条件就是if (rev INT_MAX / 10 || (rev INT_MAX / 10 pop 7)) { return 0; }再看负方向。INT_MIN -2147483648去掉末尾后是-214748364。如果rev -214748364下一步一定溢出如果rev -214748364就要求pop不小于-8因为-214748364 * 10 (-8) -2147483648刚好是INT_MIN而pop -8时比如-9结果会小于INT_MIN溢出。负方向的判断条件if (rev INT_MIN / 10 || (rev INT_MIN / 10 pop -8)) { return 0; }如果你嫌麻烦在C语言里也可以先用long存结果统一判断后再强转回int。但LeetCode原题的环境不允许使用64位整数习惯了用long偷懒会导致你在一些要求严格的场景下写不出代码。所以建议老老实实学会提前判断。3.3 完整代码与执行流程模拟把上面的推导整合成C语言代码#include limits.h int reverse(int x) { int rev 0; while (x ! 0) { int pop x % 10; x / 10; if (rev INT_MAX / 10 || (rev INT_MAX / 10 pop 7)) { return 0; } if (rev INT_MIN / 10 || (rev INT_MIN / 10 pop -8)) { return 0; } rev rev * 10 pop; } return rev; }为什么要在更新rev之前判断因为溢出发生在rev * 10 pop这一步等它算完再判断程序可能已经返回了未定义行为的结果。提前判断是保险丝。拿x 1534236469模拟一下初始rev 0第一次pop 9x 153423646rev 9第二次pop 6x 15342364rev 96...到rev 964632435时下一次循环pop 1此时rev INT_MAX / 10964632435 214748364直接返回0。完全符合预期。这个例子非常典型就是专门用来检验溢出判断写没写对的。4. 语言差异与实战避坑4.1 Python的负数取模是个坑如果你改用Python写这道题照抄C语言的%和/逻辑马上就会翻车。原因在于Python的取模和整除规则跟C语言不一样。Python中%的结果总是非负的//是向下取整而不是向零截断所以-123 % 10 7而不是-3-123 // 10 -13而不是-12这意味着什么如果你直接写pop x % 10当x -123时拿到的末位是7而不是3整个翻转结果就乱了。解决办法主要有两种。第一种是统一转成正数处理def reverse(x: int) - int: sign 1 if x 0 else -1 x abs(x) rev 0 while x: pop x % 10 x // 10 rev rev * 10 pop rev * sign return rev if -2**31 rev 2**31 - 1 else 0第二种是保留负号但手动调整取模结果def reverse(x: int) - int: rev 0 while x ! 0: pop int(x % 10) # 兼容负数的手动取末位 x int(x / 10) # 注意这里不能用 // if rev 2**31 // 10 or (rev 2**31 // 10 and pop 7): return 0 if rev -2**31 // 10 or (rev -2**31 // 10 and pop -8): return 0 rev rev * 10 pop return rev但说句实话Python里用字符串方案是最省心的因为Python的大整数没有溢出问题你只需要在最后和32位边界比较就行。真正需要抠%和/语义的是C/C/Java这类有严格整型范围的语言。4.2 JavaScript和Java里的特殊陷阱JavaScript没有真正的32位整型所有数字都是浮点数但位运算会先把操作数转成32位有符号整数。这意味着如果你用x | 0去强制截断2147483648 | 0会变成-2147483648这就是JS里独特的溢出表现。比较稳妥的方案是var reverse function(x) { let rev 0; const INT_MAX 2147483647; const INT_MIN -2147483648; while (x ! 0) { const pop x % 10; x Math.trunc(x / 10); // 必须用trunc不能用Math.floor if (rev Math.floor(INT_MAX / 10) || (rev Math.floor(INT_MAX / 10) pop 7)) return 0; if (rev Math.ceil(INT_MIN / 10) || (rev Math.ceil(INT_MIN / 10) pop -8)) return 0; rev rev * 10 pop; } return rev; };Java的做法则更“富余”一些。因为Java有异常机制可以用Math.addExact或Math.multiplyExact捕获溢出异常class Solution { public int reverse(int x) { int rev 0; while (x ! 0) { int pop x % 10; x / 10; try { rev Math.multiplyExact(rev, 10); rev Math.addExact(rev, pop); } catch (ArithmeticException e) { return 0; } } return rev; } }这个写法代码短、逻辑清晰是Java专属的“作弊器”。但在面试时建议先讲明白手写判断的原理再补一句“Java还可以用异常”显得你既懂原理又懂语言特性。4.3 常见错误与排查速查表我统计了一下大家在这道题上最常见的几个翻车点做成一个速查表方便你写完后自查。错误场景典型代码出错原因解决方案重置前未判断溢出rev rev * 10 pop;溢出发生在赋值时在累加前判断rev与INT_MAX/10的关系用long后仅在最后判断long ans...; if(ansINT_MAX) return 0;某些环境禁止64位中间值改用提前判断不依赖64位忘记处理末尾0用字符串翻转后直接int()int(021)会自动去0但字符拼接时要小心数学拆位天然处理负数用abs()后忘记恢复符号while(x){...}; return rev;负数翻转后符号丢失用sign标记或保持负数运算Python用//处理负数整除x // 10向下取整导致死循环或错位转正数处理或改用int(x / 10)-2147483648取绝对值abs(INT_MIN)溢出-INT_MIN超出int范围保持负数运算不要取绝对值那句“保持负数运算”值得展开一下。为什么-2147483648的绝对值是个坑因为2147483648已经超出了32位有符号整数的最大值如果你在C或Java里执行x -x它会溢出。所以在C语言的标准解法里我们压根不把负数转正直接依赖负数取模的规则去处理反而是最安全的。5. 从这道题延伸出去的思考5.1 同类型题目的思路迁移整数翻转的解题框架可以无缝迁移到好几道LeetCode题目上。最直接的是第9题“回文数”。判断一个整数是否是回文不需要真的把整个数翻转出来比较你只需要翻转一半数字然后比较前半部分和后半部分是否相等。这个“翻转一半”的实现技巧就是用x % 10取出末位、累加到revertedNumber上跟整数翻转几乎一模一样。还有第8题“字符串转换整数 (atoi)”它需要你一边扫描字符串一边累加数字并且随时判断是否超出INT_MAX和INT_MIN这里的溢出判断逻辑同样可以用“预先比较再拼数字”的方式实现。再比如第190题“颠倒二进制位”虽然操作对象从十进制变成了二进制但思路完全一致取最低位、结果左移、原数右移。你会发现当你把整数翻转吃透后这些题都只是换了外壳。5.2 刷题复盘的真实心得最后聊点刷题之外的东西。这道题我前前后后写过不下五遍每次都是在不同时间点重新做每次都能发现新的细节。第一遍稀里糊涂过了第二遍才发现负数取模的坑第三遍才真正理解为什么INT_MAX / 10能作为判断边界第四遍才意识到原题“不得使用64位整数”这条约束的良苦用心。所以如果你的第一版代码没写好不用气馁这个题本来就值得你反复写。我的一个建议是AC之后别急着庆祝做一次“三问复盘”——第一个问题我有没有用到题目里的所有约束条件第二个问题如果换一种语言写我现在的代码还成立吗第三个问题如果去掉现成的API不能转字符串、不能用long我还能写出来吗这三个问题都能答上来这道题才真正算你消化了。刷题拼的不是数量是你对每一道基础题挖得有多深。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价