回文数是指正序(从左向右)和倒序(从右向左)读都相同的整数。例如,121 是回文数,而 -121 和 10 不是。本文将介绍两种解法:字符串转换法和反转一半数字法,并分析它们的复杂度。
解法一:字符串转换法
思路
将整数转换为字符串,然后使用双指针从两端向中间比较字符是否相同。如果所有字符都相同,则是回文数;否则不是。
步骤
-
处理负数:负数直接返回
false
(因为有负号)。 -
转换为字符串:将整数转为字符串。
-
双指针比较:
-
左指针从字符串头部开始,右指针从尾部开始。
-
依次比较左右指针的字符,若不同则返回
false
。 -
若所有字符相同,则返回
true
。
-
复杂度
-
时间复杂度:O(n),其中 n 是数字的位数。需要遍历一半字符串。
-
空间复杂度:O(n),存储字符串需要额外空间。
class Solution { public boolean isPalindrome(int x) { // 负数直接返回 false if (x < 0) { return false; } // 将整数转为字符串 String numStr = String.valueOf(x); int left = 0; // 左指针 int right = numStr.length() - 1; // 右指针 // 双指针向中间移动,比较字符 while (left < right) { if (numStr.charAt(left) != numStr.charAt(right)) { return false; // 字符不同,不是回文数 } left++; right--; } return true; // 所有字符相同,是回文数 } }
解法二:反转一半数字法(性能最高的解法)
反转一半数字法是性能最优的解决方案。它通过数学运算只反转数字的后半部分,并与前半部分比较,避免了字符串转换和完全反转的溢出风险。
算法原理
-
处理特殊情况:
-
负数直接返回
false
-
非零且末位为0的数(如10)直接返回
false
-
-
反转后半部分数字:
-
当剩余数字 > 反转数字时循环
-
每次取原数字末位拼接到反转数字末尾
-
原数字移除末位
-
-
比较两部分:
-
偶数位:反转部分 == 剩余数字
-
奇数位:反转部分/10 == 剩余数字(忽略中间位)
-
性能分析
-
时间复杂度:O(log₁₀n)
-
循环次数 = 数字位数/2
-
例如:12321 只需循环2次
-
-
空间复杂度:O(1)
-
仅使用常数级变量
-
class Solution {
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的末位
x /= 10;
}
/* 比较两部分:
1. 偶数位数:x == reversedHalf
2. 奇数位数:x == reversedHalf/10(忽略中间位) */
return x == reversedHalf || x == reversedHalf / 10;
}
}
优势说明
-
最优时间复杂度:仅需 O(log₁₀n) 时间
-
常数空间:无额外数据结构
-
避免溢出:只反转一半数字,确保不会溢出
-
数学运算高效:除法和取模操作是CPU基础指令
边界处理
-
最大整数测试:2147483647 → 非回文数
-
最小负数测试:-2147483648 → 直接返回false
-
零处理:0 → 满足x==reversedHalf(0==0)
-
末位零处理:10/20/100等 → 开头检测直接返回false