LeetCode 9简单数学
回文数 图解题解
这道题到底在问什么
给定整数 x,判断它是否是回文整数。进阶要求:不把它转成字符串。
- 输入
- x = 121 → true(正读反读都是 121)
- 输入
- x = -121 → false(负号只在左边)
- 输入
- x = 10 → false(末尾 0 不能当首位)
最优解:一步一步想明白
- 3转字符串很直观,但要额外开一段空间,而且面试常要求不转字符串。纯数学法能做到常数空间。
- 4不反转整个数,只反转后一半。循环停下时,x 是左半边、rev 是反转过的右半边,两边一比就知道是不是回文。
- 5if x < 0 or (x % 10 == 0 and x != 0): return False负数不对称,直接 false。末尾为 0 的非零数反转后首位会是 0,也直接 false。x=0 是唯一例外,最后会被放行。
- 6rev = 0rev 从 0 开始。每轮把 x 最低位弹出来接到 rev 后面。循环条件是 x 大于 rev,保证只处理后一半。
- 7rev = rev * 10 + x % 10; x //= 10弹出最右边的 1,接到 rev 后面,rev 变 1。x 砍掉末位变 1232。x 还远大于 rev,继续。
- 8rev = rev * 10 + x % 10; x //= 10再弹出 2,rev 变 12(右边的 21 已经被反转成 12)。x 变 123,还是大于 rev,继续。
- 9rev = rev * 10 + x % 10; x //= 10奇数位的中间那位 3 也被弹进了 rev,rev=123。x 变 12,现在 x 小于 rev,循环停止——后一半反转完了。
- 10return x == rev or x == rev // 10奇数位时 rev 多带了中间的 3。x==rev 不成立,但去掉中间位后 rev//10=12,正好等于左半 x=12,所以 12321 是回文。
- 111221:rev 和 x 直接相等,不用去中间位偶数位没有中间位:弹两次后 x=12、rev=12 直接相等。所以 x==rev 管偶数位,x==rev//10 管奇数位,一行覆盖两种。
- 12x=10:x % 10 == 0 且 x != 0 → 直接 return False如果不挡 10:反转得到 01,整数里前导 0 会丢掉变成 1,1 不等于 10、却也不好比。所以末尾为 0 的非零数提前 false。
- 15两个要点:一是只反转后半,省运算又避开整型溢出的思路风险;二是奇偶统一成 x==rev 或 x==rev//10。
- 22学完回文数去整数拆位专题连刷:lc7 整数反转用同样的取末位拼接,lc202 快乐数也靠拆位循环。卡住时用 AI 答疑问『我的奇偶判断哪里不稳』。进专题:/leetcode-animation/ds?k=bit
⚠️ 容易写错的地方
✗ 错:忘记排除负数
✓ 对:x 小于 0 直接 return false
负号只在左边,倒过来位置就变了,负数永远不是回文。
✗ 错:10、100、1000 误判为回文
✓ 对:x % 10 == 0 且 x != 0 直接 return false
末尾为 0 的非零数反转后首位是 0,整数会丢掉前导 0,比较必错。
✗ 错:奇数位只判断 x == rev
✓ 对:还要判断 x == rev // 10
奇数位时中间那位会多跑进 rev,要用整除 10 去掉它再比。
完整代码(Python / C++ / Java)
Python
class Solution:
def isPalindrome(self, x: int) -> bool:
if x < 0 or (x % 10 == 0 and x != 0):
return False
rev = 0
while x > rev:
rev = rev * 10 + x % 10
x //= 10
return x == rev or x == rev // 10C++
class Solution {
public:
bool isPalindrome(int x) {
if (x < 0 || (x % 10 == 0 && x != 0))
return false;
int rev = 0;
while (x > rev) {
rev = rev * 10 + x % 10;
x /= 10;
}
return x == rev || x == rev / 10;
}
};Java
class Solution {
public boolean isPalindrome(int x) {
if (x < 0 || (x % 10 == 0 && x != 0))
return false;
int rev = 0;
while (x > rev) {
rev = rev * 10 + x % 10;
x /= 10;
}
return x == rev || x == rev / 10;
}
}套路模板 · 整数半反转骨架(挖空背诵)
# 步骤 1:排掉不可能是回文的——负数 / 末尾 0 的非零数
if x < 0 or (x % 10 == 0 and x != 0):
return False
# 步骤 2:只反转后一半,rev 赶上 x 就停
rev = 0
while x > ____: # 填:rev
rev = rev * 10 + x % 10
x //= 10
# 步骤 3:偶数位 x==rev;奇数位去掉中间位
return x == rev or x == ____ # 填:rev // 10// 步骤 1:排边界
if (x < 0 || (x % 10 == 0 && x != 0)) return false;
// 步骤 2:只反转后一半
int rev = 0;
while (x > /*填*/ rev) {
rev = rev * 10 + x % 10;
x /= 10;
}
// 步骤 3:奇偶统一
return x == rev || x == /*填*/ rev / 10;// 步骤 1:排边界
if (x < 0 || (x % 10 == 0 && x != 0)) return false;
// 步骤 2:只反转后一半
int rev = 0;
while (x > /*填*/ rev) {
rev = rev * 10 + x % 10;
x /= 10;
}
// 步骤 3:奇偶统一
return x == rev || x == /*填*/ rev / 10;复杂度
时间复杂度
O(log n)
x 的位数是 log10(x);每轮砍掉一位、只处理后一半,约循环 log10(x)/2 次。
空间复杂度
O(1)
只用 rev 一个整数变量,不开字符串、不开数组。
看不够?换成动画再走一遍
上面的推演每一步都对应一帧动画。点开交互动画版,能一步步看着 回文数 的数据怎么变、指针怎么走,还能切 Python / Java / C++ 跟着练。
面试官可能追问
为什么只反转后一半,不反转整个数?+
反转整个数可能超出 32 位范围(比如以 8、9 开头的大整数反转就溢出)。只反转后一半,数值最多到原数的一半,不会越界,也少跑一半循环。
这道题为什么用「数学」,换最直接的暴力解会差在哪?+
数学抓住了本题的结构特征,把暴力解里重复的工作省掉;暴力解通常要多嵌套一层枚举,数据一大就超时。具体对比见上文「暴力解及其卡点」与「最优解逐步推演」两节。
本平台为独立第三方培训机构,与华为技术有限公司无任何关联;课程的服务内容与权益以购买协议为准,学习效果因个人情况而异。「华为 OD」「华为可信」等仅为对岗位与考试方向的客观描述,相关商标归各自权利人所有。