问题描述
给定一个整数 x,判断它是否是回文数。回文数是指正序(从左向右)和倒序(从右向左)读都是一样的整数。
示例 1:
输入:x = 121
输出:true
示例 2:
输入:x = -121
输出:false
解释:从左向右读为 -121,从右向左读为 121-,因此不是回文数。
示例 3:
输入:x = 10
输出:false
解释:从右向左读为 01,因此不是回文数。
进阶要求: 你能不将整数转为字符串来解决这个问题吗?
解法一:字符串转换法(简单直观)
这是最直观的解法,将整数转换为字符串,然后用双指针比较。
java
public boolean isPalindrome(int x) {
// 将整数转换为字符串
String str = String.valueOf(x);
int left = 0, right = str.length() - 1;
// 双指针向中间移动比较
while (left < right) {
if (str.charAt(left) != str.charAt(right)) {
return false;
}
left++;
right--;
}
return true;
}
复杂度分析:
-
时间复杂度:O(n),其中 n 是数字的位数
-
空间复杂度:O(n),需要额外的字符串存储空间
优缺点:
-
✅ 代码简单,易于理解
-
❌ 需要额外空间创建字符串
-
❌ 不符合进阶要求
解法二:完全反转数字法
不转换为字符串,直接通过数学运算反转整个数字,然后与原数字比较。
java
public boolean isPalindrome(int x) {
// 负数不可能是回文数
if (x < 0) {
return false;
}
int reversed = 0;
int original = x; // 保存原始值
// 反转数字
while (x != 0) {
// 每次取出x的个位数,加到reversed的末尾
reversed = reversed * 10 + x % 10;
x /= 10; // 去掉x的个位数
}
// 比较反转后的数字与原始数字
return reversed == original;
}
复杂度分析:
-
时间复杂度:O(log₁₀x),循环次数等于数字的位数
-
空间复杂度:O(1),只使用了常数级别的额外空间
注意事项:
这种方法有一个潜在问题:反转后的数字可能超出int的范围(虽然对于回文数判断来说,溢出不会影响结果正确性)。
解法三:反转一半数字法(最优解)
这是最高效的解法,只需要反转数字的一半,然后与另一半比较。
java
public boolean isPalindrome(int x) {
// 特殊情况:
// 1. 负数不是回文数
// 2. 以0结尾的数(0本身除外)不是回文数
if (x < 0 || (x % 10 == 0 && x != 0)) {
return false;
}
int reversedHalf = 0;
// 当原始数字大于反转数字时,继续循环
while (x > reversedHalf) {
// 将x的个位数加到reversedHalf的末尾
reversedHalf = reversedHalf * 10 + x % 10;
x /= 10; // 去掉x的个位数
}
// 对于偶数位数字:x == reversedHalf
// 对于奇数位数字:x == reversedHalf / 10(去掉中间位)
return x == reversedHalf || x == reversedHalf / 10;
}
复杂度分析:
-
时间复杂度:O(log₁₀x/2),只需要反转一半的数字
-
空间复杂度:O(1),只使用了常数级别的额外空间
为什么这是最优解?
-
避免了完全反转可能导致的溢出问题
-
减少了一半的循环次数
-
空间复杂度最低
-
代码简洁,逻辑清晰
性能对比
| 方法 | 时间复杂度 | 空间复杂度 | 优点 | 缺点 |
|---|---|---|---|---|
| 字符串转换 | O(log₁₀x) | O(log₁₀x) | 简单直观 | 额外空间开销 |
| 完全反转 | O(log₁₀x) | O(1) | 空间最优 | 可能溢出 |
| 反转一半 | O(log₁₀x/2) | O(1) | 最优解 | - |
边界情况处理
在实现回文数判断时,需要考虑以下边界情况:
-
负数:所有负数都不是回文数
-
末尾为0:除了0本身,所有以0结尾的数字都不是回文数
-
整数溢出:在反转数字时要注意溢出问题
-
单个数字:0-9都是回文数
面试建议
在面试中遇到这个问题时,我建议:
-
先问清楚要求:是否需要考虑负数?是否允许转换为字符串?
-
从简单方法开始:先提出字符串转换法,展示基本思路
-
逐步优化:提出完全反转数字法,分析可能的问题(溢出)
-
提出最优解:展示反转一半数字法,解释为什么这是最优解
-
讨论边界情况:展示对边界情况的考虑
-
准备其他解法:如果时间允许,可以提一下数学方法
常见面试问题
-
"为什么反转一半数字的方法更好?"
- 答:因为它避免了完全反转可能导致的溢出问题,并且减少了一半的计算量。
-
"如何处理负数?"
- 答:根据回文数的定义,负数都不是回文数,因为负号在前端。
-
"如果数字特别大,你的方法还能工作吗?"
- 答:反转一半数字的方法不会反转整个数字,所以不会溢出。对于数学方法,我们只比较最高位和最低位,也不会溢出。
总结
判断整数是否是回文数有多种解法,每种解法都有其适用场景:
-
日常编程:字符串转换法最简单直接
-
面试场景:反转一半数字法最优,体现了对问题的深入理解
-
算法竞赛:根据具体限制条件选择合适的方法
掌握多种解法不仅能帮助我们解决具体问题,更能提升我们分析问题和优化算法的能力。