问题描述
给定一个32位有符号整数 x,返回将其数字部分反转后的结果。如果反转后整数超出32位有符号整数范围 [-2³¹, 2³¹-1],则返回0。
示例:
-
输入:123 → 输出:321
-
输入:-123 → 输出:-321
-
输入:120 → 输出:21
-
输入:1534236469 → 输出:0(反转后9646324351 > 2147483647)
基础算法:反转数字
1. 核心思路
通过循环取模 运算获取数字的最后一位 ,然后推到结果rev的末尾:
java
while (x != 0) {
int digit = x % 10; // 获取最后一位
rev = rev * 10 + digit; // 最后一位推入结果数的末尾
x /= 10; // 移除x最后一位
}
2. 处理负数
Java的取模运算对负数也有效:-123 % 10 = -3,因此上述代码可以直接处理负数。
核心挑战:整数溢出
1. 为什么需要处理溢出?
32位有符号整数的范围是 -2147483648 到 2147483647。反转后的数字可能超出这个范围,例如:
1534236469反转后为9646324351>2147483647
2. 溢出检查的原理
我们需要在执行 rev = rev * 10+ digit 之前进行检查,因为一旦计算完成,溢出已经发生。
检查条件推导:
对于正数溢出:
-
如果
rev > Integer.MAX_VALUE / 10,那么rev * 10肯定溢出 -
如果
rev == Integer.MAX_VALUE / 10,那么需要digit ≤ 7(因为Integer.MAX_VALUE = 2147483647)
对于负数溢出:
-
如果
rev < Integer.MIN_VALUE / 10,那么rev * 10肯定溢出 -
如果
rev == Integer.MIN_VALUE / 10,那么需要digit ≥ -8(因为Integer.MIN_VALUE = -2147483648)
完整解决方案
方案一:详细边界检查(推荐)
java
class Solution {
public int reverse(int x) {
int rev = 0;
while (x != 0) {
int digit = x % 10;
// 检查正数溢出
if (rev > Integer.MAX_VALUE / 10) {
return 0;
}
if (rev == Integer.MAX_VALUE / 10 && digit > 7) {
return 0;
}
// 检查负数溢出
if (rev < Integer.MIN_VALUE / 10) {
return 0;
}
if (rev == Integer.MIN_VALUE / 10 && digit < -8) {
return 0;
}
rev = rev * 10 + digit;
x /= 10;
}
return rev;
}
}
方案二:官方简化版
java
class Solution {
public int reverse(int x) {
int rev = 0;
while (x != 0) {
if (rev < Integer.MIN_VALUE / 10 || rev > Integer.MAX_VALUE / 10) {
return 0;
}
int digit = x % 10;
x /= 10;
rev = rev * 10 + digit;
}
return rev;
}
}
官方版的巧妙之处:
看似省略了等于边界的检查,但实际上当 rev 等于边界值时,如果 digit 会导致溢出,计算后的 rev 会在下一次循环中被检查出来。虽然更简洁,但逻辑不够直观。
方案三:使用long类型(面试友好)
java
class Solution {
public int reverse(int x) {
long rev = 0;
while (x != 0) {
rev = rev * 10 + (x % 10);
x /= 10;
// 检查是否超出int范围
if (rev > Integer.MAX_VALUE || rev < Integer.MIN_VALUE) {
return 0;
}
}
return (int) rev;
}
}
常见错误分析
错误1:计算后检查溢出
java
// 错误!溢出已经发生
rev = rev * 10 + digit;
if (rev > Integer.MAX_VALUE) { // 此时rev已经是错误的值
return 0;
}
错误2:错误的检查条件
java
// 错误!任何int除以10都不会超过MAX_VALUE
if (rev / 10 > Integer.MAX_VALUE) {
return 0;
}
错误3:忽略等于边界的情况
java
// 不完整!缺少对rev == 边界值/10 的检查
if (rev > Integer.MAX_VALUE / 10) {
return 0;
}
数学原理深度解析
1. 边界值分析
text
Integer.MAX_VALUE = 2147483647
Integer.MAX_VALUE / 10 = 214748364
当 rev = 214748364 时:
rev * 10 = 2147483640
最大可加的 digit = 2147483647 - 2147483640 = 7
2. 为什么检查要在计算前?
因为整数溢出在Java中不会抛出异常,而是会"回绕":
-
2147483647 + 1 = -2147483648 -
但我们希望提前检测到这种情况并返回0
面试技巧与话术
1. 逐步解决问题
-
"首先,我会实现基本的数字反转逻辑"
-
"然后,我需要考虑负数的处理"
-
"最后,最重要的是处理整数溢出问题"
2. 解释检查逻辑
可以说:
"为了避免溢出,我在计算 rev * 10 + digit 之前进行检查:
-
如果
rev已经大于MAX_VALUE/10,那么乘以10后肯定溢出 -
如果
rev等于MAX_VALUE/10,那么需要确保digit不超过7"
3. 处理不确定的情况
如果不记得精确值:
"我不记得边界值的最后一位是7还是8了,但我知道原理:当 rev 等于边界值的1/10时,需要检查 digit 是否超过边界值的个位数。"
性能分析
-
时间复杂度:O(log₁₀|x|),循环次数与数字的位数成正比
-
空间复杂度:O(1),只使用常数空间
扩展思考
1. 如果输入是64位整数?
原理相同,但边界值变为 Long.MAX_VALUE 和 Long.MIN_VALUE
2. 如何反转浮点数?
需要处理小数点和精度问题,思路完全不同
3. 其他语言中的实现
不同语言对整数溢出的处理方式不同:
-
C/C++:未定义行为
-
Python:整数自动扩展,不会溢出
-
Java:溢出会回绕
总结
整数反转问题是一个很好的算法练习,它考察了:
-
基础编程能力:循环、取模、除法运算
-
边界条件处理:负数、零、最大值、最小值
-
溢出预防:在计算前进行检查
-
代码健壮性:考虑所有可能的输入情况
关键要点:
-
必须在计算前进行溢出检查
-
正数和负数的溢出检查逻辑不同
-
边界情况(
rev == 边界值/10)需要特殊处理 -
使用long类型可以简化实现,但可能不符合某些题目要求
无论是面试还是日常编程,正确处理边界条件都是优秀程序员的重要标志。整数反转问题虽然简单,但其中的细节值得深入思考和掌握。