LeetCode 7. 整数反转
题目描述
✅ 7. 整数反转

题意分析
输入是一个 32 位有符号整数
x,要求把它的十进制数位左右颠倒后返回。真正的难点不在「颠倒」,而在于颠倒之后的值可能已经不是一个 32 位整数了,这时必须返回0。题面里有一句决定性的约束信号:假设运行环境只能存储 32 位有符号整数,范围是 $[-2^{31}, 2^{31} - 1]$。这句话的意思是「不允许先算出结果再回头检查」——因为一旦算出来,那个越界的中间值本身就已经不存在了,你手上拿到的是一个被回绕过的错误数字。所以判断必须发生在计算之前。
三个边界要在读题阶段就想清楚。第一,负号不参与反转,
-123的答案是-321而不是321-,符号原样保留。第二,末尾的零在反转后会跑到最高位,而最高位的零是不存在的,所以120的答案是21,位数会变少,这不是 bug 而是十进制的固有性质。第三,x本身可能是0,此时循环一次都不进,直接返回0。还有一个容易被忽略的不对称:
-2147483648的绝对值超出了正数上界,所以任何「先取绝对值、算完再补符号」的写法在这个输入上都会当场翻车。安全的做法是让负数从头到尾保持负数。
解法:逐位弹出并提前判溢出
核心思路
问题关键:逐位反转本身很简单,难点是题目只允许使用 32 位整数。
ans * 10 + digit一旦先发生溢出,事后看到的已是回绕后的错误值,因此必须在运算前判断。为什么选逐位弹出:
digit = x % 10取出最低位,x /= 10删除最低位,再用ans = ans * 10 + digit将它追加到结果。Java 和 Go 对负数除法都向 0 截断,负数可以沿用同一套逻辑,不必先取绝对值。不变量:每轮开始时,
ans是已弹出数位的反转结果,并且仍在 32 位范围内;x保存尚未处理的高位。追加新数位前,先保证ans * 10 + digit不会越界。边界推导:正向越界条件是
ans > MAX/10,或ans == MAX/10且digit > 7;负向对应ans < MIN/10,或相等时digit < -8。这些比较只使用尚未溢出的值。正确性:每轮把当前最低位追加到
ans末尾,因此处理 t 位后,ans恰是原数末尾 t 位的逆序;循环结束时所有数位都已处理,得到完整反转值。若某轮触发边界条件,该次追加必然超出 32 位范围,按题意返回 0。
解题步骤
- 初始化
ans = 0,当x != 0时循环;该条件同时覆盖正数和负数。- 先用
% 10取最低位,再用/= 10删除该位。- 根据
MAX/10、MIN/10及末位 7、-8,判断追加后是否溢出;溢出立即返回 0。- 安全时执行
ans = ans * 10 + digit,循环结束返回ans。- 口述样例:
-123依次弹出-3、-2、-1,ans依次为-3、-32、-321。1534236469在最后一次追加前触发上界判断,返回 0;120得到 21,前导零自然消失。
代码实现
class Solution {
public int reverse(int x) {
int ans = 0;
while (x != 0) {
int digit = x % 10;
x /= 10;
if (ans > Integer.MAX_VALUE / 10
|| (ans == Integer.MAX_VALUE / 10 && digit > 7)) {
return 0;
}
if (ans < Integer.MIN_VALUE / 10
|| (ans == Integer.MIN_VALUE / 10 && digit < -8)) {
return 0;
}
ans = ans * 10 + digit;
}
return ans;
}
}
const (
maxInt32 = 1<<31 - 1
minInt32 = -1 << 31
)
func reverse(x int) int {
ans := 0
for x != 0 {
digit := x % 10
x /= 10
if ans > maxInt32/10 || (ans == maxInt32/10 && digit > 7) {
return 0
}
if ans < minInt32/10 || (ans == minInt32/10 && digit < -8) {
return 0
}
ans = ans*10 + digit
}
return ans
}
复杂度分析
- 时间复杂度:$O(\log \lvert x\rvert)$,循环次数等于十进制位数;32 位整数最多 10 位。
- 空间复杂度:$O(1)$,只使用固定数量的整数变量。
关键点总结
- 溢出检查必须在乘 10 之前,并同时覆盖“商越界”和“商相等但末位越界”。
- 不要对
Integer.MIN_VALUE取绝对值,它的正数值无法用 32 位int表示。- Java、Go 的
%会保留被除数符号,因此负数无需单独处理。- 面试若允许 64 位中间变量,可以先用
long计算再判断;本写法严格满足题目的 32 位限制。
易错点总结
- 先计算再判溢出:
1534236469会先发生整数回绕,之后已无法恢复正确结果。- 遗漏相等时的末位判断:通用 32 位累积模板必须检查上界末位 7 和下界末位 -8。
- 先取绝对值:
Math.abs(Integer.MIN_VALUE)仍是负数,不能用这种方式统一符号。- 循环条件写成
x > 0:-123将直接返回 0;应写x != 0。- 先除 10 再取模:会丢掉原来的最低位,
123将错误得到 21。
相似题目
| 题目 | 难度 | 考察点 |
|---|---|---|
| 9. 回文数 | 简单 | 只反转一半数位即可比较,无需处理溢出 |
| 8. 字符串转换整数 (atoi) | 中等 | 溢出时钳制到边界值而非返回 0,还需解析前后缀 |
| 190. 颠倒二进制位 | 简单 | 位宽固定为 32,反转的是二进制位且不会溢出 |
| 29. 两数相除 | 中等 | 禁用乘除法,靠倍增减法逼近商 |
| 66. 加一 | 简单 | 数位存在数组里,关注进位导致的扩容 |
| 43. 字符串相乘 | 中等 | 用数组模拟竖式,彻底回避整数范围限制 |