今天是我开始刷 LeetCode Hot 100 的第一天进度是 2/100。如果你也打算从这 100 道题启动自己的算法之路或者已经在第一天被各路题解砸得头晕目眩这篇内容想跟你聊聊新人第一天到底该怎么刷哪些坑值得提前知道以及一个真正能走完 100 题的节奏大致长什么样。说实话Hot 100 这个名字在算法圈子里几乎等于面试必刷清单但很多新人点开第一题就开始怀疑人生——明明题目看懂了思路也有就是写不出来或者写出来了又超时。我跟你说这太正常了。我自己第一次刷 Two Sum 的时候暴力枚举算法写得飞快一提交直接超时然后才知道哈希表这个东西的存在。那种感觉就像你以为自己在解题其实只是在翻译题目。这篇文章没有高深的理论就是一个过来人把第一天的真实过程拆给你看为什么选 Hot 100、那两道题我具体怎么想、新人最容易在哪三个地方栽跟头、以及 2/100 之后的路怎么排。无论你是刚学完数据结构、准备找实习还是单纯想提升代码能力这篇都能给你一个能直接照着走的起点。1. 为什么算法新人第一站选 Hot 100而不是从 2000 题开始挖1.1 Hot 100 不是题目集合而是一份考点地图我见过太多新人打开 LeetCode 题库看到 2000 多道题直接懵了。今天刷一道简单题明天刷一道 hard后天再看题解发现连贪心、动态规划、图论全混在一起两周之后唯一的感觉就是刷了个寂寞。Hot 100 的本质不是 100 道题而是把面试中最高频的考查点浓缩成了 100 个样本。数组、哈希表、链表、栈、二叉树、回溯、贪心、动态规划、图论、堆、字符串处理这些数据结构与算法核心专题在里面都能找到代表题。换句话说你把 Hot 100 吃透等价于把面试官最喜欢问的那部分知识地图走了一遍而不是在荒原上乱撞。这个价值对新人来说尤其大。新人最缺的不是刷题量而是优先级。Hot 100 直接帮你划好了重点你不需要自己去判断哪道题值得花时间因为这份清单本身已经是被筛选过的。1.2 从简单题硬啃到 hard 题劝退是新人最常见的死法有一种常见路径是先挑简单题刷刷了 50 道简单题觉得自己稳了然后去 open contest 被一道中等题打回原形。另一种反过来一上来直接挑战 hard被按在地上摩擦一天第二天就放弃了。Hot 100 的难度分布相对温和简单和中等占大头hard 就那十来道新人完全可以按难度梯度推进。更重要的是这 100 道题里绝大多数都能从暴力解起步然后一步步优化到最优解。这种先跑通再优化的节奏恰好是算法学习里最健康的正反馈循环。我第一天定的目标很简单不贪多两道题必须亲手写出来不看题解先自己想。事实证明这个节奏比一天 10 道题靠谱太多了。1.3 100 题的规模刚好匹配人类大脑的记忆周期每天 2 题50 天刷完第一轮配合复习和复盘三个月左右可以完成一个非常扎实的循环。100 这个数字不大不小大到能覆盖足够多考点小到你能在放弃之前看到进度条在动。我自己的体感是刷到第 20 题左右会有明显的开窍感刷到第 50 题时已经能对大部分题目形成条件反射式的直觉。如果你第一周就被题目淹没那大概率不是能力问题而是选题策略出了问题。所以别嫌 Hot 100 太普通扎扎实实把它走完比任何花哨刷题法都管用。2. Day 1 的两道题拆解从暴力解到最优解我到底想了什么Day 1 我选的是 Two Sum两数之和和 Valid Parentheses有效的括号。这两道题一个考哈希表一个考栈都是最基础也最高频的数据结构而且都适合新手从暴力枚举一路推到最优解。下面我把每一步思考过程还原给你。2.1 第 1 题 Two Sum哈希表是空间换时间的第一课题目很简单给定一个整数数组nums和一个目标值target在数组中找出和为目标值的两个数返回它们的下标。任何新人看到这道题第一反应必然是暴力枚举算法两层循环外层定住第一个数内层找有没有target - nums[i]。def two_sum_brutal(nums, target): n len(nums) for i in range(n): for j in range(i 1, n): if nums[i] nums[j] target: return [i, j] return []这个解法的时间复杂度是 O(n^2)空间复杂度 O(1)。题目本身没问题但 LeetCode 的测试数据规模摆在那里n 可能到 10^5 甚至更大O(n^2) 在提交时大概率超时。我当时的优化触发点是这样一个朴素问题内层循环为什么一定要遍历能不能把我已经见过的数记下来下一次直接查于是出现了哈希表方案遍历数组时把每个数作为 key、它的下标作为 value 存进字典每次遇到一个新数num只查target - num在不在字典里。def two_sum(nums, target): seen {} for i, num in enumerate(nums): complement target - num if complement in seen: return [seen[complement], i] seen[num] i return []这里有个关键细节也是第一个大坑必须先查再存不能先存再查。如果先把nums[i]存进字典再查target - nums[i]那么当target恰好是2 * nums[i]且数组中只有一个该数时你会把同一个元素用两次直接返回错误结果。举个例子nums [3, 3]target 6。正确做法是遍历到第二个 3 时发现第一个 3 已经存在字典中返回[0, 1]。如果先存再查遍历第一个 3 时字典里还没有其他元素但你在存入后立刻查查到的是刚存的同一个 3于是返回[0, 0]——错得离谱。时间复杂度从 O(n^2) 降到 O(n)代价是多用一个 O(n) 的字典。这就是空间换时间最典型的第一课。我后来面试时这道题的变体出现过很多次比如数组有序的情况下用双指针 O(n) 空间 O(1)但这些都不是第一天该操心的事。2.2 第 2 题 Valid Parentheses用栈模拟编译器的括号匹配题目给定一个只包含( ) [ ] { }的字符串判断括号是否有效。有效定义是左括号必须用相同类型的右括号闭合并且按正确顺序闭合。这道题在 Hot 100 里的地位很高因为栈这种数据结构在真实世界的典型应用就是括号匹配和表达式求值。你甚至可以把它理解成一个简化版编译器在检查语法。新手最容易想到的直觉是后出现的左括号必须先被匹配。比如([)]只看数量是对的两个左、两个右但顺序错了因为第二个左括号[还没被匹配右括号)却先来了。而这种后进先出的需求天然就是栈的用武之地。我的写法是维护一个字典做映射然后遍历字符串遇到左括号就入栈遇到右括号就检查栈顶是不是对应的左括号是则弹出否则直接返回 False。def is_valid(s): pairs {): (, ]: [, }: {} stack [] for ch in s: if ch in pairs: if not stack or stack[-1] ! pairs[ch]: return False stack.pop() else: stack.append(ch) return not stack这里有两个边界问题我在第一天就栽过第一右括号到来时栈已经空了说明前面没有与之配对的左括号直接返回 False。这个判断not stack不能省否则stack[-1]会抛异常。第二遍历结束后不能直接返回 True得检查栈是否为空。比如输入(()前半截都匹配成功但最后栈里还剩一个(说明存在未闭合的左括号。这个解法的时间复杂度 O(n)空间复杂度 O(n)因为最坏情况下全是左括号所有字符都要进栈。2.3 为什么这两道题是新人第一天的黄金组合选这两道题不是随机的。第一道题让你体验从 O(n^2) 暴力解到 O(n) 最优解的跃迁第二道题让你第一次认真接触栈这种结构化的数据流处理。两者都不涉及复杂的动态规划或贪心策略却覆盖了面试中两个最高频的基础数据结构哈希表和栈。而且这两道题都有一个共同点从暴力解到最优解之间的推导链条非常短。新人不需要额外看十篇题解才能理解只要肯多问自己一句能不能把已经见过的信息记下来就能顺理成章地推出最优解。这种正反馈能帮你建立难得的信心。3. 新人第一天最该注意的三个坑边界、重复和复杂度直觉3.1 边界条件不是面试官刁难它是题目定义的一部分我在第一天的惨痛教训就是代码逻辑写对了但一提交就报错。原因全是边界条件没考虑。Two Sum 的边界是nums为空、只有一个元素、没有符合的解Valid Parentheses 的边界是空字符串、单个字符、只包含左括号或只包含右括号、以及括号交叉嵌套。一个非常实用的习惯在写代码之前先在草稿纸上列好测试用例。比如 Valid Parentheses 我会先写()应该 True()[]{}应该 True(]应该 False([)]应该 False{[]}应该 True应该 True[应该 False)(应该 False。把这些用例列完再写代码你会发现逻辑一下就清晰了。因为边界条件不是额外要处理的事它就是题目逻辑的一部分。你写代码的时候如果心里想着这个栈空的情况会不会出现代码自然就不会漏。3.2 重复元素和不能重复用同一个元素是个容易嘴硬写错的细节Two Sum 里有个经典表述不能重复使用同一个元素。很多人觉得这不是废话吗但真到写代码的时候先插再查就会踩坑。我再强调一遍先查后存还是先存后查不是一个风格问题而是一个正确性问题。你可以在草稿纸上模拟nums [2, 2, 4]target 4看看两种写法分别返回什么。先查后存得到[0, 1]先存后查有可能返回[0, 0]甚至[1, 1]。这个问题在笔试里尤其阴因为很多同学紧张时手一快就写反了。我一个很深的体会是这类小细节才是算法面试真正筛选人的地方。思路大方向大家都懂区别就在于边界处理是否严谨。3.3 复杂度直觉为什么 O(n^2) 在 LeetCode 上会超时新人经常困惑一个问题为什么我的代码逻辑没问题LeetCode 却报超时这就要建立复杂度直觉。现代计算机每秒大概能执行 10^8 到 10^9 次简单操作但 LeetCode 的测试数据往往卡得很紧。你可以用下面这个粗略参考值来判断输入规模 nO(n) 耗时O(n^2) 耗时结论10^3纳秒到微秒级毫秒级都能过10^4微秒级约 0.1 秒O(n^2) 可能卡时间10^5毫秒级约 1 秒以上O(n^2) 大概率超时10^6毫秒级约 100 秒O(n^2) 必超时所以你写完代码第一件事不是提交而是看一眼数据规模。如果n是 10^5你还在用双重循环那基本可以预判超时了。这个习惯我从第一天开始就刻意训练后来看到任何题目都会先瞄一眼 constraints再决定解法方向。第一天的主要练习目标其实不是多解几道题而是把这三种意识种进脑子里边界、重复元素、复杂度判断。这三样东西比背二十个模板都好使。4. 2/100 之后的事100 题不是 100 个孤立题目而是一套可循环的系统4.1 别按原顺序从头刷到尾按主题分块效率更高很多人打开 Hot 100 列表就从第 1 题刷到第 100 题但我建议你换个方式先按主题分类同类型的题目集中刷。比如第一周只刷哈希表相关的题第二周只刷栈和队列第三周刷链表第四周刷二叉树。这样做的原因是同类型题目的解题模式高度相似你在一周内反复使用同一个数据结构记忆会非常牢固。怎么判断当前题目属于哪个主题很简单看题解之前先看题目描述如果要找最近的匹配的成对的大概率跟栈有关如果要用之前见过的值加速查找大概率跟哈希表有关如果涉及树的遍历那就是二叉树专题。第一天做完这两道题你已经能区分哈希表题和栈题了。4.2 每天两题不是硬性指标三轮刷题法才是核心我给自己定的执行框架是三轮刷题法第一轮按主题刷允许自己看题解但看完必须关闭题解自己完整写一遍然后隔天再独立写一遍。第一轮的目的是积累常见套路不用要求自己闭卷。第二轮限时独立做。每道题只给自己 30 到 40 分钟想不出来直接看题解然后记录自己卡在哪一步。这一轮的目标是逼迫大脑在压力下调用已有知识。第三轮专项总结。把同一个专题的题目放在一起对比它们的相同点和不同点形成自己的套路笔记。每天两题的节奏是为了配合这个框架。我见过有人一天刷十题但因为完全没有复习环节一周后忘得干干净净等于白刷。宁可每天只做两道也要保证做过变成会做。4.3 间隔重复让做过变成会做的关键人类记忆曲线的规律是学完一个东西如果不复习20 分钟后遗忘 40% 以上一天后遗忘 70% 以上。所以刷题笔记必须记录一个复习时间表。我用的笔记模板是这样的日期xx题号 / 题目xx难度简单 / 中等 / 困难我的解法思路 复杂度最优解法思路 复杂度错误点边界 / 重复元素 / 复杂度误判复习计划第 1 天 / 第 3 天 / 第 7 天 / 第 14 天第 1 天就是当天第二次复习最好放在第 3 天第三次在第 7 天第四次在第 14 天。这个间隔重复的策略是我踩过坑之后才学到的方法。最初我刷完题就丢在一边两周后连题目长什么样都忘了更别提解法。这个方法听起来特别朴素但真的有效。把复习写入计划之后100 题就不再是 100 个孤立的记忆负担而是一条带着自动记忆曲线的学习系统。4.4 遇到做不出来的题千万别死磕超过一小时第一天刷题大概率不会一帆风顺。我的建议是15 分钟原则。一道题如果想了 15 分钟毫无头绪直接看题解看懂之后关闭题解自己写一遍然后记录卡点比如你是想不到用哈希表还是想不到用双指针。不要觉得看题解丢人暴力解、看题解、模仿、复现、独立实现这个循环本身就是学习算法的正常路径。真正的问题不是看不看题解而是看完之后有没有亲手写出来。如果你看了十道题解一道都没有自己独立写过那才是白费功夫。遇到特别难的题尤其是 hard可以暂时跳过先刷同专题的中等题。但是跳过的题要记在一个回访清单里等到第一轮结束后的第二轮再来硬啃。这样既不会卡死进度也不会留下知识盲区。第一天如果被一道题卡了两小时大概率后面的计划就崩了。所以我强烈建议新人把节奏控制得像呼吸一样自然能写就写写不出就学学完就记记完就复习。说回第一天本身。我到现在还能清晰复述出看到 Two Sum 哈希解法时那种原来还能这样的震动那大概就是算法入门最好的驱动力。Day 1 这两道题我后来在面试场合原封不动遇到过也见过不少人倒在 Valid Parentheses 的栈空判断上。所以别嫌题简单第一天把这两道题吃透比囫囵吞枣刷十道题有用得多。明天的 2/100 和今天的 2/100差的不是那两个数字是你在边界和复杂度上积累的那一点点手感。