LeetCode第8题实战:字符串转换整数(atoi)与边界条件解析
发布时间:2026/9/9 18:51:18 锦皓数字建站
与边界条件解析`)
1. 一个“100题计划”的起点为什么我决定这样刷题先说个背景。我给自己定了一个 LeetCode(8/100) 的刷题计划意思是100道题刷到第8道。这个计划不是随手拍的是认真想过的。如果你也在刷题或者准备跳槽面试你应该有同感LeetCode 的题太多了3500多道就算一天刷3道也要三年人根本不可能刷完。但真正面试高频的、核心的题目其实就那么150到200道集中在数组、链表、树、动态规划、二分查找、DFS/BFS、字符串处理这些大类里。所以我给自己定的规则很简单只刷经典题单不追求题量追求每一道都吃透。我这100题就是从热门100题、面试高频题、周赛经典题里筛出来的按难度和知识点做了排序前10题不碰难题先把基础的数据结构和常见套路过一遍形成“肌肉记忆”。第8题就在这个阶段里。说实话前几题刷起来很容易让人上头因为全是简单题ACAccepted率很高你会觉得自己马上就要成为算法大神了。但越往后越发现真正拉开差距的不是AC而是“能不能写清楚思路”“能不能举一反三”。所以这个系列我打算每一篇都复盘一道题把思路、代码、坑、同类题都讲透不搞那种“看答案抄一遍”的假努力。今天这篇正好到了第8题。这题叫“字符串转换整数 (atoi)”如果你看过 LeetCode 题库应该知道它是第8题。题目本身不复杂但细节极其多是典型的“边界条件魔王”题非常适合用来做刷题方法论的样本。2. 第8题实战拆解字符串转换整数atoi2.1 题目到底在考什么先还原一下题目要求。输入是一个字符串需要实现一个myAtoi(string s)函数把字符串转成 32 位有符号整数。规则有几点跳过开头空格处理正负号读入连续数字遇到非数字就停止如果数字超过 int 范围就做截断如果没有有效数字就返回0。听上去是不是很直白但真写起来你会发现坑一个接一个。我在第一次提交时就栽在“正负号只能出现一次”和“ 后面必须紧跟数字”这两个细节上。这道题在 LeetCode 上被归类为“字符串处理”但它的本质是“有限状态机”。什么意思呢就是你处理每一个字符时当前状态决定了下一步能做什么。比如你已经进入了“正在读数字”的状态再遇到空格就不是跳过的空间而是“结束”的触发条件。如果你已经设置了读入负号再遇到加号就不能当作正负号处理而是要当作非法字符结束。我在做题时习惯把这类题单拎出来因为它的思路不复杂但特别考验严谨程度。面试里出类似的题考察的就是你有没有“穷举所有边界条件”的思维习惯。2.2 主流解法状态机 vs 条件判断实际刷题时这个题有两种主流写法。第一种是“条件判断流”就是直接撸一个if-else链依次处理空格、符号、数字、结束。逻辑直接代码量不大但容易漏条件。LeetCode 官方题解里也给了这种写法适合对题意非常熟的人。第二种是“状态机法”先定义几种状态比如 start、signed、in_number、end然后用一个映射表或 switch 分支来处理每个字符过来时应该跳到哪个状态。代码会稍微长一点但可读性好思路清晰也方便以后做扩展。我推荐用第二种原因很简单状态机把复杂的边界问题拆成了“状态 x 字符”的二维判断不容易漏。下面是这道题我用 C 写的一个实现版本class Solution { public: int myAtoi(string s) { int i 0, n s.size(); while (i n s[i] ) i; int sign 1; if (i n (s[i] || s[i] -)) { if (s[i] -) sign -1; i; } long long res 0; while (i n isdigit(s[i])) { res res * 10 (s[i] - 0); if (res INT_MAX) { return sign 1 ? INT_MAX : INT_MIN; } i; } return sign * (int)res; } };这段代码主要处理了三个细节跳过前导空格这是第一步判断符号位注意和-都要处理读入数字的过程中提前判断溢出用long long做中间量。你可能会问为什么中间量要用long long直接用int不就行了吗不行。因为在res res * 10 digit这一行数字可能已经超过 int 范围了在赋值给int之前就会发生溢出产生未定义行为。用long long中间过渡一下确保我们能安全判断是否越过边界。一个小优化是题目明确要求“超过 int 范围就截断到边界值”所以不需要等读完整个数字再判断而是在累加过程中实时判断一旦越界就立刻返回可以省掉一些无用的计算。2.3 我踩过的坑和测试用例这道题最值得聊的就是它那堆“反直觉”的测试用例。我自己跑的时候总结了一批你可以直接拿去自测输入预期输出坑点说明4242标准情况 -42-42前导空格要跳过4193 with words4193数字后面出现字母就停words and 9870开头不是数字就返回0-91283472332-2147483648下溢截断到 INT_MIN-420正负号不能同时出现 420符号和数字之间有空格也算非法-0只有符号没有数字0空字符串 0全是空格第6、7、8这几个用例是最容易挂的。我第一次写的时候只考虑到了“正负号后面接数字”但没考虑“正负号后面接非数字字符”的情况。比如 42按题意应该返回0但如果你的逻辑是先读符号、再跳空格那就会错误地返回42。还有-42我当时是“遇到符号位就设置 sign”结果处理完之后又遇到-直接把 sign 改了最后返回 -42。实际上应该是在第一个符号之后第二个符号被视作非法字符直接结束解析。我的建议是做完这个题之后把所有官方题解里的测试用例全部手动跑一遍再自己多构造几个像 0 123、00000000000000000000042这样的极端输入确保万无一失。这道题多花一点时间是很值的因为它在各个公司的面试里出现频率非常高。3. 刷题指南前10题应该怎么安排才能见效3.1 前10题覆盖哪些核心考点每次有人问“LeetCode 刷题指南到底怎么刷”我都建议前10题别贪多也别碰难题。我给自己前10题的安排是这样的基础数据结构 常见思维模型。具体来说就是数组两数之和、链表反转链表、字符串atoi、二分查找爱吃香蕉的狒狒那种二分答案、动态规划入门杨辉三角/爬楼梯、DFS/BFS 的简单题。这些题看起来简单但它们覆盖了刷题最常用的几种思考框架哈希表用空间换时间快速查找。双指针处理有序数组或链表的常用手段。递归与回溯树的遍历、排列组合问题的基础。二分查找不止用于有序数组还可以“二分答案”比如“爱吃香蕉的狒狒”就是典型例题。动态规划先写暴力递归再优化成递推建立状态转移方程的意识。很多人一开始刷题就扎进“动态规划”和“图论”上来就刷难题结果挫败感特别强一个题写一下午最后还得看题解。我的经验恰恰相反前10题最重要的是建立“能独立 AC”的自信。一旦你连续几天都是在别人的代码里“重新发明轮子”你的刷题动力会断崖式下降。3.2 如何把100题拆解成可执行的小目标我给自己的100题做了一个拆解逻辑前30题打基础中30题练套路后40题综合实战。具体拆分如下第1-10题数组、字符串、链表的基础操作热身为主第11-30题哈希表、栈、队列、双指针、滑动窗口练常见套路第31-60题二分查找、DFS/BFS、树的遍历、回溯开始做中等题第61-80题动态规划专项从简单到中等每天只刷一道但要彻底搞懂第81-100题综合题高频面试题覆盖前面积累的多个知识点。这样的好处是每一阶段都有明确目标不会出现“今天不知道刷什么”的迷茫。LeetCode 的题单非常多我主张你固定用一个题单不要来回切换。来回切题单最大的问题是知识体系会变得零散今天刷链表明天刷动态规划后天又回去刷链表看似很努力实际上各个知识点都没有形成系统。我的做法是每天固定刷2到3道题第一道是“复习题”从错题本里挑一道重做第二道是“当天新题”第三道是“探索题”从新题出发找一到两道相似的同类型题快速过一遍。这样既保证了新知识的摄入也把旧知识的遗忘曲线拉平了。4. 近期几道经典题目复盘从热搜里学套路4.1 爱吃香蕉的狒狒二分答案的经典模板这是 LeetCode 第73题热词里提到“073爱吃香蕉的狒狒”题面是狒狒每小时能吃一堆香蕉中的k根如果一堆不足k根就全部吃掉并花完这一小时要求在h小时内吃完所有香蕉求最小的k。这题我第一次看到是在周赛题单里当时第一反应是“直接模拟”从 k1 开始试试到能吃完为止。但是 n 和 piles 的范围一上来就知道暴力不行。标准解法是二分答案对 k 做二分每次检查一个 k 能不能在h小时内完成任务。这类题的套路非常固定你可以把它当作模板背下来bool canFinish(vectorint piles, int k, int h) { long long hours 0; for (int p : piles) { hours (p k - 1) / k; if (hours h) return false; } return true; } int minEatingSpeed(vectorint piles, int h) { int l 1, r *max_element(piles.begin(), piles.end()); while (l r) { int mid l (r - l) / 2; if (canFinish(piles, mid, h)) { r mid; } else { l mid 1; } } return l; }注意两个细节一个是hours要用long long因为p k - 1算出来可能超出 int 范围另一个是判断条件“能在 h 小时内完成就缩小右边界”因为我们要找的是最小可行 k所以要不断往左逼近。跟这道题同类的还有“分割数组的最大值”“第K个魔法数字”等它们都是二分答案。读完全文你可以试着用同一个模板去套这些题一旦你能一眼看出“这题是二分答案”刷题效率会大幅提升。4.2 目标和从 DFS 到动态规划“目标和”是一道我非常喜欢的中等题。题目是给你一个非负整数数组nums和一个目标值target你可以给每个整数前面添加或-问有多少种不同的表达式结果等于target。这个题第一眼看上去像个 DFS 枚举题每个数字选加或选减总共2^n种情况。n 很大的时候暴力肯定超时。这时候就要用动态规划。你把它转化一下思路假设所有添加的数字和为P所有添加-的数字和为N那么P N sum(nums)P - N target两式相加得到P (sum target) / 2。所以问题变成了有多少种子集它们的和等于(sum target) / 2。这就是经典的 01 背包问题。转换的过程是这道题最重要的“为什么”。如果只是背代码下次遇到变体你还是不会做。做题的价值正在于此——通过一道题掌握一个思考角度把符号选择问题转化成子集和问题再落到背包DP上。4.3 最长回文子串中心扩展法“最长回文子串”也是热词里非常高频的一道题。回文串就是正读和反读一样的字符串比如aba。题面要求在一个字符串s里找到最长的回文子串。这个题有三种主流解法暴力枚举O(n^3)、动态规划O(n^2)、中心扩展O(n^2)、Manacher算法O(n)但是难写。我建议面试时掌握前三种Manacher了解即可因为面试官一般不期待你在15分钟内写对Manacher。中心扩展法的思路特别直观每个回文串都有一个“中心”中心可能是一个字符奇数长度比如aba的中心是b也可能是两个字符之间偶数长度比如abba的中心是bb之间。所以遍历所有可能的中心点向两边扩展记录最长的那一个。class Solution: def longestPalindrome(self, s: str) - str: def expand(l, r): while l 0 and r len(s) and s[l] s[r]: l - 1 r 1 return s[l1:r] res for i in range(len(s)): odd expand(i, i) even expand(i, i 1) res max(res, odd, even, keylen) return res这个代码很短但它能帮你理解“回文串的扩张性质”。后续很多字符串题像“回文子串数量”“最长回文子序列”都会用到类似的思路。4.4 周赛430里那些“不该错”的题关于“leetcode周赛430”我没法罗列具体题目内容因为每场比赛题目都会变但我在周赛中的复盘经验是有共性的。周赛和平时刷题最大的不同在于时间压力每题只有20分钟左右写错了还要调试容错率极低。我通常会给自己定一个策略前两题必须在15分钟内AC第三题最多40分钟如果超过40分钟就放弃去看题解。第四题通常涉及高级数据结构或复杂DP能写多少写多少不为难自己。周赛对于刷题计划的帮助在于它能逼你在“烂熟”和“理解”之间做出区分。一道题你之前刷过换个马甲出现在周赛里你能不能认出来这就是检验刷题效果的最好方式。5. 刷题工具和习惯我推荐的时间与错题管理方式5.1 必装的几个工具工欲善其事必先利其器。我用到的工具就三样浏览器版 LeetCode、本地编辑器VS Code、以及一个在线的 Markdown 笔记软件。LeetCode 官网自带题解、讨论区、测试用例而且可以查看提交记录。刷题不像写业务代码它需要快速迭代本地写再粘贴到网页提交其实效率不高我后来干脆直接在网页编辑器里写。VS Code平时用来跑用例、做实验。特别是遇到需要调试的复杂题网页的调试能力还是弱了一些。Markdown 笔记每道题记录四块内容题目链接、我的第一遍思路、正确答案的思路、以及我踩的坑。截图也可以放进去。工具不在多关键是形成闭环做题、复盘、记录、重做。没有闭环的话你刷的题会在半个月后变成“似曾相识但写不出来”。5.2 错题本的正确用法很多人也记错题但把错题本做成了“抄题本”把题目、代码全部复制一遍之后再也不看。这没有任何意义。我的用法是只记录“我为什么错”和“我应该怎么想”。比如第8题 atoi我会这样记录错误点正负号后面出现空格时我错误地跳过了空格继续读数字正确思路一旦进入“已读符号”状态下一个字符必须是数字否则无有效数字对应教训字符串处理题必须画状态图或者至少把状态转移的边界条件列出来。然后我会在刷到第20题左右的时候重新做一遍错题。如果重做时依然卡住说明我对那个知识点的理解还不够需要找2到3道同类型题强化。这个“重做”的环节才是错题本真正发挥价值的地方。5.3 每天刷几题适合大多数人我见过两种极端一种是每天只刷1题刷到第50题就忘了前30题另一种是全天泡在题海里一天刷10题但每道题都是看答案默写。两种都很难坚持到100题。适合大多数人的频率是工作日1到2题周末3到4题。按这个速度100题大概需要8到10周正好两个多月。这个节奏不会过度消耗意志力又能保证知识点的有效积累。我自己会用“番茄钟”来限时新题30分钟超时直接看题解复习题15分钟做不出来就回到错题本重新分析。这个做法有点像周赛的模拟训练长期坚持下来正式笔试或面试时对时间的感觉会准很多。6. 刷到第8题后我对算法学习的新认识说实话第8题这种“看似简单、实则暗礁遍布”的题目比偏难怪题对我的帮助更大。它逼我意识到一个问题算法题并不是考验你知不知道某个高深技巧而是考验你能不能把一个工程问题拆解成清晰、无歧义的步骤。比如 atoi 的边界条件面试时考官不是看你记没记住INT_MAX是2147483647而是看你能不能在面对未知输入时用系统的方式来穷举可能性。在第8题之前我刷题更像是“撞墙式”看到题目蒙个方向写代码提交超时或报错再看题解。第8题之后我开始改变策略先把状态或边界条件列出来再动笔。哪怕是一道简单的链表反转题我也会先想清楚“如果头节点是空怎么办”“如果只有两个节点怎么办”。这个习惯说来很简单但真的到了考场上能让你少挂在很多“阴间用例”上。另外我还想说的是100题计划的意义不在于把100道题的答案背下来。它更像是一张地图每个节点是一种题型刷完100个节点你就把常见的知识图谱给过了一遍。以后遇到新题你也许不会马上写出完整答案但你至少能快速定位到“这个题属于哪个大类的哪个分支”然后用对应的套路去试探。这就是所谓的题感。以我目前的进度第8题只是一个开始。后面还有92道题等在那里有动态规划、有图论、有各种贪心。我知道越往后越难中途肯定会有想放弃的时候。但目前看来这个方法论的框架是对的——用经典题单缩小范围用状态拆解对付边界条件用错题本对抗遗忘用周赛检验能力。如果你也和我一样正在刷题或者正准备开始希望你不用太焦虑进度也不用盲目追求题量。碰到不会的题停下来把它的套路拆干净比多刷十道“看一眼就忘”的题更有用。我们到第9题、第10题的时候再继续聊。
锦
锦皓数字建站
深耕本土企业品牌数字化升级,专注原创端正雅致商务官网,从视觉设计到稳定运维全程保驾护航。