
力扣第9题回文数。这大概是所有题库里最容易被新手一眼带过的题目题干短、通过率高、代码可能三行就写完。但真把它放到“从零开始刷力扣”这个系列的第一站你会发现它比想象中值得琢磨。先说结论这道题不仅考察最基本的整数处理、字符串操作和边界意识还是后面一大串回文类题目的地基。搞清楚这一题后面遇到“验证回文串”“最长回文子串”“回文链表”时你至少能少踩一半的坑。很多刚开始刷力扣的同学有个误区一上来就想挑战中等难度甚至困难题觉得简单题没有含金量。但我在带新人、也带自己的过程中反复确认过一件事——简单题里藏着的边界条件、数学性质和语言特性往往比难题的算法模板更值得背下来。回文数就是最典型的例子它用最少的代码量把“你习不习惯处理边界条件”这件事暴露得干干净净。1. 为什么把“回文数”作为刷题第一站这道简单题的分量1.1 一道简单题为什么能常驻热题榜你打开力扣的题目列表按热度排序回文数通常排在前列。这不是偶然。它简洁、高频、有明确的判定标准而且几乎所有主流算法教材都会把它作为整数的入门操作练习。对面试来说它出现的频率不算低尤其在后端岗的笔试环节里经常作为第一道签到题存在。但比“面试会不会考”更重要的是它是一道能用来检验“基础功”的题。怎么做字符串反转、怎么取整数的每一位、怎么处理负数、怎么规避溢出——这四件事几乎是所有算法题的地基操作。回文数一道题全给覆盖了。所以哪怕你已经刷了几十道题再回头看这题也值得用最优解重新写一遍。1.2 从“能AC”到“懂原理”之间隔着一整个方法论我见过不少同学看到这题直接写一行return str(x) str(x)[::-1]交上去通过然后下一题。这样刷刷三百题和刷三十题的区别可能只是手速变快了。这就是我想说的AC 只是起点理解和表达才是这一题真正的考点。你能不能用非字符串的方式解能不能解释清楚为什么x % 10 0时可以直接返回 false能不能在面试官追加一句“不能用额外空间”的时候马上切换思路这三个问题才是这一题能否成为你算法基石的判定标准。所以接下来我们把每一层都拆开看。2. 题目陷阱拆解负数、末尾0与回文定义的边界2.1 回文数的定义正序倒序读完全一致先看题目原文给你一个整数x如果x是一个回文整数返回true否则返回false。回文数是指正序从左向右和倒序从右向左读都是一样的整数。这句话看起来没有任何歧义但落进代码里就有问题了。正序是121倒序还是121没问题。正序是-121倒序按字符读是121-不一致所以-121不是回文数。这是很多人第一个栽的地方想当然地把负号忽略掉或者去比较绝对值。错了负号是数字的一部分只要x 0直接返回false即可不需要再做任何操作。2.2 两个最容易踩的边界用例-121 和 10第二个边界是末尾为 0 的数。比如10正序是10倒序是01也就是1显然不一致。再比如100倒过来是001也不是回文。但这里有一个隐藏细节如果一个非零正整数以 0 结尾那么它倒过来之后必须以 0 开头而正常整数不会以 0 开头所以它永远不可能是回文数。这个结论可以直接转换成代码优化if x 0 or (x % 10 0 and x ! 0): return False注意x ! 0这个条件不能省因为0本身以 0 结尾但它是回文数。这个“末尾为0但本身是0”的特例我在实际代码评审里至少见到过三次被漏掉。2.3 确定边界条件x % 10 0 的数学含义x % 10 0的本质是“这个数能不能被10整除”。一个数能被10整除说明它在十进制表示下末位是0。一个回文数的末位和首位必须是同一个数字而首位不允许是0所以末位也不能是0。唯一例外就是这个数本身就是0。这套推理很多人在高中学数论的时候其实见过但刷题时不会主动用。我建议你把这类“先数学判断再进入算法过程”的思维养成习惯。它不只是在回文数里有用判断闰年、判断素数、处理进制转换都能用同一套思路省下大量无意义计算。3. 三种解法的完整推演从直觉到最优3.1 解法一字符串反转一行代码解决但别止步于此最直接的思路是把整数转成字符串再反转比较class Solution: def isPalindrome(self, x: int) - bool: s str(x) return s s[::-1]这段代码在力扣上能通过而且可读性极高。但你要清楚它做了什么先把整数转成字符串然后创建一个新的反转字符串再逐一比较。时间复杂度是 O(n)其中 n 是数字的位数空间复杂度也是 O(n)因为额外创建了字符串对象。这里有个面试官常追问的点“能不能不用字符串”这不是故意刁难。很多场景下语言提供的字符串反转是有额外内存开销的如果数据量极大或者系统对内存敏感你就需要一个纯数学的方案。所以解法一适合作为第一反应但不适合作为唯一答案。3.2 解法二整数整体反转考虑溢出才是重点既然不能用字符串那我们直接在整数层面做反转。受“反转一个整数”这道题的启发我们可以把x的每一位依次取出拼成一个新数再判断是否与原数相等class Solution: def isPalindrome(self, x: int) - bool: if x 0: return False num x rev 0 while num ! 0: rev rev * 10 num % 10 num // 10 return rev x这个思路很干净num % 10取出当前个位rev * 10 个位把数字倒着拼回去num // 10去掉已经取过的个位。循环结束后rev就是x反转后的整数。但这里有一个关键问题如果在 C 或 Java 里rev可能在过程中超过 int 的最大值。比如x 2147483647反转后是7463847412这已经超出int范围了。虽然在 Python 里整数是任意精度的不会报错但如果你用 Java 写int rev 0; while (num ! 0) { rev rev * 10 num % 10; num / 10; }编译器不会警告你运行时却可能溢出。这就是为什么官方题解更推荐只反转一半数字而不是全量反转。在实际面试场景里你能主动说出“如果我先反转整个数在强类型语言里可能溢出所以我会选择反转一半”这本身就是加分项。3.3 解法三反转一半数字最优解的核心思路核心思想如果一个数是回文数它的后半部分反转后应该和前半部分相等。比如1221后半部分21反转成12前半部分正好也是12。对于奇数长度的12321后半部分321反转成123去掉最后一位3后等于12前半部分也是12。实现逻辑如下class Solution: def isPalindrome(self, x: int) - bool: if x 0 or (x % 10 0 and x ! 0): return False reverted 0 while x reverted: reverted reverted * 10 x % 10 x // 10 return x reverted or x reverted // 10手动跑一遍1221初始reverted 0x 1221。第一次循环reverted 0 * 10 1 1x 122。此时x (122) reverted (1)继续。第二次循环reverted 1 * 10 2 12x 12。此时x (12) reverted (12)不成立退出。判断x reverted即12 12返回true。再跑一遍12321初始reverted 0x 12321。第一次reverted 1x 1232。第二次reverted 12x 123。第三次reverted 123x 12。此时x (12) reverted (123)不成立退出。因为原数长度是奇数中间位3会落到reverted的最后一位所以判断x reverted // 10即12 12返回true。为什么中间循环条件用x reverted因为当我们把后半部分反转过来一旦后半部分已经超过前半部分就说明已经处理到一半了。这个终止条件很优雅既不会少处理一位也不会多处理导致重复。3.4 三种方案的复杂度与适用场景对比方案时间复杂度空间复杂度核心优点潜在风险字符串反转O(n)O(n)代码最短逻辑直观依赖语言特性额外内存开销整数整体反转O(n)O(1)纯数学操作不依赖字符串强类型语言中可能溢出反转一半数字O(n/2)O(1)天然规避溢出官方推荐需要额外处理奇偶长度我的建议是面试时优先说反转一半的思路但前两种也要会写。因为面试官经常从简单题开始热身然后逐步追加条件比如“不允许转字符串”“能不能用 O(1) 空间”。你能按顺序给出三种方案本身就是对这道题理解程度的最好证明。4. 代码实现与实测从会读到能写4.1 Python 版本最贴近思维的实现力扣上最常见的 Python 解就是反转一半的版本完整代码如下class Solution: def isPalindrome(self, x: int) - bool: if x 0 or (x % 10 0 and x ! 0): return False reverted 0 while x reverted: reverted reverted * 10 x % 10 x // 10 return x reverted or x reverted // 10这段代码有几个值得注意的地方。首先是x % 10 0 and x ! 0这个边界判断确保10、100、1000这类数直接返回false。其次是x // 10在 Python 3 中//是整数除法不会出现浮点误差。最后是返回语句中的或运算同时处理偶数长度和奇数长度两个情况。如果你追求极致的简洁也可以用字符串一行流class Solution: def isPalindrome(self, x: int) - bool: return str(x) str(x)[::-1]这段代码能 AC但它对负数的处理依赖字符串的-号-121反转后是121-两者不等所以也正确。但我在代码评审里不会推荐它作为“最优解”因为它没有体现你对整数运算和空间复杂度的理解。4.2 Java 和 C 版本溢出处理是加分项用 Java 写反转一半class Solution { public boolean isPalindrome(int x) { if (x 0 || (x % 10 0 x ! 0)) { return false; } int reverted 0; while (x reverted) { reverted reverted * 10 x % 10; x / 10; } return x reverted || x reverted / 10; } }和 Python 版本几乎一一对应。唯一要注意的是 Java 对整数除法的处理x / 10对于正数就是去掉末位不会有向负无穷取整的困扰因为我们已经提前排除了负数。C 版本也很类似class Solution { public: bool isPalindrome(int x) { if (x 0 || (x % 10 0 x ! 0)) { return false; } int reverted 0; while (x reverted) { reverted reverted * 10 x % 10; x / 10; } return x reverted || x reverted / 10; } };这里有一个看似不起眼但很关键的点reverted * 10会不会溢出即使在反转一半的情况下reverted的长度也只有原数的一半最多是 10 位量级乘 10 之后的位数仍然在可控范围内。对比整体反转方案反转一半在数学上就规避了大多数溢出场景这也是官方推荐它的根本原因。4.3 完整测试用例清单与易错点复盘我把这道题值得跑的测试用例拉了一张表建议你提交之前逐项过一遍测试输入期望结果说明121true基本回文数-121false负号导致不对称10false末尾为0的非零数0true0是回文数注意别被末尾0条件误杀1true单个数字永远是回文11true偶数长度最短回文1001true偶数长度中间为00100false以0结尾且非012321true奇数长度回文2147483647falseint最大值反转会溢出的情况2147447412true接近int最大值的回文数每次提交前用这个清单过一遍能明显减少“看到 WA 才发现漏了边界”的情况。我最常看到新手犯的错误就是把x % 10 0 and x ! 0写漏导致10这个用例挂掉还有就是在奇数长度时只写了x reverted导致12321挂掉。这两个坑你提前在这道题里踩过后面遇到类似问题就会天然有警觉。5. 一道题带出一张“回文题族”地图刷题顺序的参考思路5.1 回文在字符串和链表场景中的变体回文这个概念不只存在于数字里。你刷到后面会遇到这些同族题目字符串回文力扣 125 题“验证回文串”。给定一个字符串只考虑字母和数字字符忽略大小写判断它是否是回文串。这题虽然也可以用字符串反转来做但更推荐的解法是双指针从左右两端向中间逼近遇到非字母数字就跳过。它的核心思路和回文数很接近但多了一个“字符过滤”的步骤。最长回文子串力扣 5 题“最长回文子串”这是一道中等题但思路可以很优雅。最简单的做法是中心扩展法枚举每一个可能的回文中心然后向两边扩展。它和回文数一样也需要处理奇偶长度的问题——奇数的中心是一个字符偶数的中心是两个字符之间的空隙。如果你在回文数里把“奇偶长度分开处理”这个思想练熟了这题会顺手很多。回文链表力扣 234 题“回文链表”。给你一个单链表的头节点判断它是否为回文链表。链表不能像数组一样随机访问所以常见的解法是快慢指针找中点然后反转后半段链表再逐一比较。这里的“找中点”和“反转链表”两步本质上是把回文数的数学处理换成了链表操作。能把这题做明白的人再回头看回文数会明显感觉难度不在一个层次上。回文对力扣 336 题“回文对”是更进阶的题需要配合哈希表或字典树来做。当你能把回文数、验证回文串、最长回文子串都刷完再来挑战回文对会形成一个完整的“回文知识块”。5.2 题型分类怎样把力扣热题100串成体系很多同学刷题顺序是乱的今天做一道数组明天做一道二叉树后天又跳到动态规划刷了三个月遇到新题还是没思路。问题不在于刷题量不够而在于没有按题型建立体系。我的建议是按“基础数据结构 基础算法”的顺序来数组和字符串基础遍历、反转、前缀和。回文数就属于这一类用最少的代码练熟悉每一个基础操作。哈希表两数之和、字母异位词分组。哈希的本质是空间换时间和回文数里“用字典统计字符频次”是一个思路。双指针和滑动窗口盛最多水的容器、无重复字符的最长子串。验证回文串就是双指针的入门场景而回文数的“反转一半”本质也带着双指针的影子。链表反转链表、环形链表。回文链表是把回文概念迁移到链表结构的实战题。二叉树前中后序遍历、层序遍历。这一类题型和回文数没有直接关系但递归思维要从这里开始训练。BFS/DFS腐烂的橘子是一道很经典的 BFS 题核心是“多源广度优先搜索”。它和回文数看似无关但你处理“一层一层向外扩展”的思路和判断回文时“从中间向两边扩展”有内在相似。动态规划爬楼梯、打家劫舍、最长回文子串。动态规划要求你定义状态和状态转移方程而这恰好是回文类题目的进阶方向。这个顺序不是绝对的但它符合大多数人的认知曲线先掌握数据结构再掌握遍历方式最后才是复杂算法设计。回文数作为第 1 题正好卡在“数组和字符串基础”这个起始环节。5.3 结合学习顺序给零基础同学的建议如果你是零基础我建议不要一上来就追求一天刷五题。更好的节奏是一道题花半天时间彻底搞懂再用半天做变体和总结。比如回文数这一题你可以给自己布置三个任务第一不看答案写出字符串反转版本第二不看答案写出反转一半版本第三和你身边的朋友或同事解释清楚“为什么x % 10 0时可以直接排除非零数”。如果三个任务都能完成这一题就算真正吃透了。之后再去看验证回文串、最长回文子串你会发现套路是熟悉的先判断边界再选择区间最后处理奇数偶数。所谓“刷题有感觉”不是玄学而是你见过了足够多的同族变体大脑自动建立了模式库。6. 刷完这道题后我还想说的三条经验6.1 力扣刷题不是为了背题而是建立识别模式很多同学喜欢在题解区背“模板代码”我也理解毕竟有些题确实有套路。但回文数这种基础题背模板的收益很低因为它太短了短到你背下来也无法迁移。真正值得做的是理解它背后的四件事取余数取末位、整除去末位、反转拼接数字、边界条件先行。这四招在进制转换、整数反转、字符串解析里反复出现。以后你再遇到一道题先不要急着写代码试着在脑子里贴标签它考的是“数字处理”还是“字符串处理”有没有明显的边界情况能不能用数学性质先排除一部分输入。这种“识别模式”的练习只能从简单题开始积累。6.2 复杂度分析要养成肌肉记忆刷题和做项目不一样项目里你只要功能跑通通常没人追问你的接口是 O(n) 还是 O(n²)但算法题会。回文数的三种解法复杂度差异不算大因为 n 最多也就是 10 位左右字符串反转甚至跑得更快。但是当 n 变成十万、百万空间复杂度的影响就出来了。所以我建议你从这一题开始养成习惯每次提交完在代码注释里写一行复杂度分析。比如反转一半这个解法你可以标注时间 O(log₁₀ n)空间 O(1)并备注“跳过末尾0的数学判断省掉了最坏情况下的一半循环”。不要笑这个习惯坚持一年你对复杂度的直觉会远超同龄人。6.3 用错题本沉淀“边界感”最后一个建议可能听起来不像技术建议但我真的觉得它很重要准备一个错题本不要记题目代码只记自己为什么错。比如回文数这一题如果你在10上挂了就记一条“非零正数以 0 结尾时反转后首位为 0必不可能是回文数”。如果你在12321上挂了就记一条“奇数长度回文中间位会多一位需要除以 10”。边界感这个东西靠看题解是看不出来的只有靠错题一遍一遍磨出来。错题本不需要写得多华丽能让自己三个月后一眼看懂“当初为什么错”就够。最后再说一句实在的这道题如果你能把三种解法都写出来并能在面试官问“为什么反转一半不会溢出”时给出数学解释那这个系列的第一站就算真正通关了。后面还有更长的路但地基已经打牢了。