很多前端方向的同学找我聊天时,第一句话往往是:我要做页面、写组件,为什么实习面试还要刷算法?我当时准备前端实习时也是这么想的。直到第一次模拟面试被一道很基础的力扣题卡住——题不复杂,但我连“看题以后先分析边界条件、再设计解法复杂度”这一套流程都没走顺,白板代码写得乱七八糟,才意识到前端实习的算法考察不是为了让你去证明数学天赋,而是想通过代码题看你的逻辑拆解、编码习惯和沟通思路。
我后来把力扣高频题按“前端实习面试可能真正遇见的范围”做了整理,按主线刷了两轮,也拿这套思路复盘过身边同学的面经。这篇文章就把我总结的路线、题号门类、训练方法全部写出来。如果你正准备前端实习,能用它少走很多弯路。
1. 一起先定个调:前端实习面试里的算法到底考什么
先说一个反直觉的观察:前端实习面试考察的算法难度,通常比很多同学想象中的要低,但它考察的维度比单纯“解出题”要更杂。
面试官不会真让你在 45 分钟内徒手写红黑树,也不会要求你用状态压缩动态规划去解一道压轴题。前端岗位更关心你有没有扎实的数据结构基本功,能不能把一个实际问题拆成可执行的步骤,以及你的代码是否具备团队协作的基本素养——命名清楚、边界处理完整、复杂度的敏感性。所以你会看到力扣热题 100 里大量简单、中等题出现在前端面经中,真正的高难度题反而少见。
其次,前端面试的算法题经常和 JavaScript 语言本身的机制缠在一起。同一个逻辑,你用 JS 写和用 C++ 写,考察点显然有差异。
比如遇到“数组移动零”这类题,JS 里可能会有人直接想到用 filter 加 concat,思路没问题,但你要是说不清这样做的额外空间开销,或者面对超大数组时产生了多少临时引用,面试官就会追问一句“如果要求原地操作呢?”——这种追问才是真正的分水岭。
前端实习算法准备,我认为不需要追求“题海战术”。
比较合理的目标是:核心数据结构能熟练使用、常见题型有明确的破题套路、能随口说出你写的代码在时间空间上的消耗。我在后面列出的力扣题号都是照着这个目标筛出来的,不是追求数量,而是每道题背后覆盖一类考点,练熟一道,同类型的题目就能迁移。
1.1 大部分前端实习算法,考察的核心是基本功
我把前端实习面试时容易出现的算法题切成了三类。
第一类:直接考察 API 思维和数组、字符串操作。数组去重、字符串反转、找公共前缀、两数之和这类问题出现频率最高。它们背后其实是在问:你对 map、reduce、双指针、哈希表这些基础工具熟不熟。注意,这一类题看着简单,但要写出“没有 bug、没有多余循环、空间可控”的版本,并不像想象中轻松。
第二类:链表、栈、队列、二叉树等简单数据结构的操作。有些前端同学会费解,平时业务里根本不会手动创建链表,为什么要考。我的理解是,考链表是在间接考验“指针 / 引用”的概念是否清晰。JS 里对象是引用类型,链表题恰恰能把“引用指向哪里、循环终止条件是什么”这件事逼到明面上,这是编写可靠前端代码的重要内功。
第三类:动态规划和贪心。前端实习面试里,这部分大多停留在“线性 DP 入门”和“简单贪心证明题”级别。爬楼梯、买卖股票的最佳时机、最大子数组和都是常客。真正的难点不是动态规划方程,而是你有没有识别出这题可以用 DP 解出来的敏感度。
1.2 和算法岗对比,前端算法有这三个“落差”
如果你去问一位后端或算法工程师,他们准备的算法题可能覆盖很多专题:线段树、网络流、数论、复杂 DP 状态设计。前端实习真的不建议在这些方向上花大量时间,性价比不高。根据我翻面经、模拟面试、实际面试的体会,前端算法准备和你熟悉的其他岗位至少有三个落差:
- 难度上限较低:前端一般不会脱离实际场景考太偏的算法。考的最多的题,基本集中在数组、链表、树、简单递归上,偶尔上探到中等难度的动态规划。
- 对工程化表达能力要求更高:除了把题写对,面试官很在意你能不能解释每一步的作用。你的解法是否利用了 JavaScript 的特性?是否需要考虑
null、空数组以及极端输入?这些在写业务组件时同样重要。 - 高频题范围高度集中:前端面经里的算法题重复率非常高。力扣 HOT 100、剑指 Offer 中的简单和中等题,已经可以覆盖绝大多数前端实习面试。
所以准备的策略应当是:与其东一榔头西一棒子刷几百道题,不如把主线上的高频题练到能脱口而出解法的程度,再把复杂度、边界、变体都盘清楚。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 我验证过的一条清晰力扣主线:按部别按热点盲目刷
我第一轮刷题时走过弯路:每天打开力扣直接点“每日一题”或者从题号 1 开始硬刷,刷到第 50 题时就崩了——因为题型太杂,今天数组、明天图论、后天状态压缩,我脑子里的知识树没建立起来,做得越多越混乱。
后来我把典型的实习面试题按“数据结构 + 题型”重新分组,顺着一条从简单到中等的主线推进,整个复习节奏立刻顺多了。下面这份题单就是我自己整理并反复验证过的,每个分组里有题号、题型和学习目标。你可以直接照着练,不用自己再额外花力气搜题。
2.1 数组与哈希表:先把“找元素”练成肌肉记忆
这组题目很适合作为第一周的入门内容。它们难度低,但覆盖了“遍历数组 → 用容器记录状态 → 降低时间复杂度”的核心思维路径。
| 力扣题号 | 题名 | 主要练什么 |
|---|---|---|
| 1 | 两数之和 | 哈希表优化,把 O(n^2) 降到 O(n) |
| 26 | 删除有序数组中的重复项 | 原地修改数组,双指针基本功 |
| 27 | 移除元素 | 双指针覆盖思路 |
| 66 | 加一 | 从后往前处理进位,边界敏感度 |
| 136 | 只出现一次的数字 | 异或运算的妙用 |
| 169 | 多数元素 | 摩尔投票/哈希计数,拓展思维 |
| 217 | 存在重复元素 | 容器去重与时间复杂度权衡 |
这里面我最想单独聊聊第 1 题“两数之和”。很多人觉得这题简单,但它在面试中的变形非常多。最直观的写法是双重循环,但当你写完以后一定要立刻意识到复杂度是 O(n^2),然后主动提出哈希表的优化方案。这个“自己发现问题,自己提出优化”的过程,比题目本身更让面试官加分。
用 JS 写哈希解法,一个很容易踩的细节是:
javascript复制function twoSum(nums, target) {
const map = new Map();
for (let i = 0; i < nums.length; i++) {
const rest = target - nums[i];
if (map.has(rest)) {
return [map.get(rest), i];
}
map.set(nums[i], i);
}
return [];
}
注意要先查 map 再往 map 里塞当前值,否则同一个元素可能被使用两次。比如 nums = [3, 2, 4], target = 6,如果先塞入 3,第二次循环时会错把 3 + 3 当成合法答案。这题暴露出的边界意识,写业务代码时同样需要——先判断再更新状态,和组件生命周期里避免拿到过期数据是同一个道理。
第 26 题“删除有序数组中的重复项”也值得多说一句:它要求原地删除。所谓原地,就是不允许你 new 一个数组再搬过去。很多前端同学第一反应是用 Array.from(new Set(nums)),这功能上是正确的,可面试官追问“空间复杂度是多少”时,你就得能回答这引入了额外存储。真正符合要求的做法是用一个慢指针记录待写入位置,一个快指针扫描数组:
javascript复制function removeDuplicates(nums) {
if (nums.length === 0) return 0;
let slow = 0;
for (let fast = 1; fast < nums.length; fast++) {
if (nums[fast] !== nums[slow]) {
slow++;
nums[slow] = nums[fast];
}
}
return slow + 1;
}
这类题练完以后,你再去写“列表去重需要保留原数组引用”的业务逻辑,思路会清晰很多。
2.2 字符串与双指针:把边界条件练到生理反应
实习面试的第二大类高频题是字符串。
字符串本质是字符数组,所以它的处理套路和数组非常像。与此同时,字符串又有很多独立的考点:子串、回文、公共前缀。这一阶段核心练三件事:双指针缩进、滑动窗口、边界条件。
| 力扣题号 | 题名 | 主要练什么 |
|---|---|---|
| 14 | 最长公共前缀 | 纵向比较 / 横向比较,字符串边界 |
| 125 | 验证回文串 | 双指针从两端向中间收拢 |
| 344 | 反转字符串 | 双指针基础 |
| 3 | 无重复字符的最长子串 | 滑动窗口 + 哈希记录 |
| 76 | 最小覆盖子串 | 滑动窗口进阶,先放一放可后期回头刷 |
| 5 | 最长回文子串 | 中心扩展法或动态规划 |
“最长公共前缀”是我看到过很多前端面经里出现的题。它本身不难,但有一个非常容易翻车的点:如果你横向比较多个字符串,得先选定一个基准串,然后不断截短它;截短时 substring 的结束位置是 prefix.length - 1,一旦写错就会出现死循环或永远比较不完。建议你先亲手把两种解法都写一遍,再想一想如果字符串数组为空,你的代码会输出什么。能把 [""] 和 [] 区分清楚,说明边界意识过关了。
第 125 题“验证回文串”是双指针入门范例。先做好字符串清洗,把大写转小写、过滤非字母数字字符,然后用 left/right 指针往中间收。这题考察的其实是“指针相遇条件”:当 left < right 时需要继续循环,一旦改成 left <= right 也可以,但要清楚地知道为什么不会越界。前端工作里处理搜索框关键词高亮、处理富文本中的特殊字符时,这种“从左往右扫、从右往左看”的思路经常能用上。
第 3 题“无重复字符的最长子串”是典型滑动窗口入门题。前端可能接触“防抖节流”时会觉得“滑动”是个很难的概念,但放在字符串里它反而很直观:窗口右侧不断向右扩展,一旦发现重复字符,左侧就收缩到重复字符的下一个位置。窗口的内容可以用哈希集合记录。窗口在不停移动,但你实际上只遍历了字符串一次,所以整体复杂度是 O(n)。这类题写顺手后,再去理解 HTTP 传输里常见的滑动窗口协议、理解流式数据处理时的缓冲思想,也会有触类旁通的感觉。
2.3 链表:空指针与引用指向的思维可视化
链表题在前端面试中属于“看起来不常用、实际很爱考”的类型。这是因为链表能干净利落地考察后台储备的 core 知识点,而且很适合口头逐步推演。没有任何前端框架会整天让你手动 node.next,但组件树、大数据量列表的虚拟节点、路由表的链式匹配,底层逻辑都隐含类似结构。
| 力扣题号 | 题名 | 主要练什么 |
|---|---|---|
| 206 | 反转链表 | 指针修改顺序,链表中最重要的题 |
| 21 | 合并两个有序链表 | 虚拟头节点 + 归并思想 |
| 83 | 删除排序链表中的重复元素 | 基本遍历删除 |
| 141 | 环形链表 | 快慢指针判断环 |
| 876 | 链表的中间结点 | 快慢指针找位置 |
| 234 | 回文链表 | 快慢指针 + 反转链表综合 |
“反转链表”是链表题的地基。如果你能写出循环版本,并且能清楚解释为什么必须要保存 next 节点,那你的指针思维就建立起来了。核心逻辑其实只有三句话:先记录下一个节点,再把当前节点的 next 指向前一个节点,最后移动前驱和当前指针。
javascript复制function reverseList(head) {
let prev = null;
let curr = head;
while (curr) {
const next = curr.next;
curr.next = prev;
prev = curr;
curr = next;
}
return prev;
}
很多初学者第一次看到这段代码会懵:“为什么不需要手动处理 head.next = null?”因为当 curr 走到旧链表的最后一个节点时,prev 恰好是旧链表的倒数第二个节点,循环结束前 curr.next = prev 已经把最后一个节点的指向改好了。有时候你会在别人的代码里看到先加一个空的虚拟头节点,原因是为了让后续插入删除操作统一,不必单独判断“当前节点是不是头部”。
第 141 题“环形链表”则是一个极好的快慢指针例子。为什么快指针走两步、慢指针走一步,有环时就一定会相遇?因为环的长度有限,二者一旦都进入环中,就变成了经典的追击问题。每走一次,快指针相对慢指针靠近一步,所以必然追上。这个证明比直接背结论有用,面试官追问时你能说出来,才会相信你是真的理解了。
2.4 二叉树与递归:先把递归序走明白,再想优化
前端组件结构、虚拟 DOM 树、目录树、AST 语法树,全都可以抽象成树结构。
所以二叉树不仅不冷门,反而是前端面试里命中率相当高的一类题。不同的是,前端考树往往不会考太复杂的旋转与平衡,而是集中在前中后序遍历、层序遍历、深度计算、翻转、对称这些基本动作。
| 力扣题号 | 题名 | 主要练什么 |
|---|---|---|
| 144 | 二叉树的前序遍历 | 递归序理解,禁止背模板 |
| 94 | 二叉树的中序遍历 | 递归序理解,配合二叉搜索树性质 |
| 145 | 二叉树的后序遍历 | 递归序理解,可用于自底向上归并 |
| 104 | 二叉树的最大深度 | 递归返回值的设计 |
| 226 | 翻转二叉树 | 递归看成一棵完整子树再操作 |
| 102 | 二叉树的层序遍历 | 队列实现 BFS |
| 100 | 相同的树 | 同步递归比较两个结构 |
我强烈建议第一遍做这三道遍历题时不要急着用迭代写法。先把递归版写到熟练,并且能对着一个三层二叉树口述“先访问根、再访问左子树、再访问右子树”时,继续回答“左子树什么时候全部访问完”这类问题。
很多讲解会把遍历框架写成很抽象的“递归序”,但实际运行时它就是在函数调用栈里进进出出。
用 JS 来写递归版前序很简单:
javascript复制function preorderTraversal(root) {
const result = [];
function dfs(node) {
if (!node) return;
result.push(node.val);
dfs(node.left);
dfs(node.right);
}
dfs(root);
return result;
}
中序遍历可以自行套用类似的位置关系。需要特别注意的一点是:中序遍历二叉搜索树的结果是有序数组,这个性质在面试里经常被拿来当作隐含条件。前端遇到“文件目录按名称排序后输出”这类需求时,如果目录用的是二叉搜索树存,那中序输出就是天然正确的答案。
第 226 题“翻转二叉树”看起来像段子,实际上非常能验证你有没有建立“子树”的递归视角。整棵树的翻转 = 根节点的左右子树交换 + 递归翻转左子树 + 递归翻转右子树。
你要能说出递归的终止条件是什么、交换发生在递归前还是递归后、对结果又有什么影响。这里交换前和交换后其实都能写出正确答案:先交换再递归,和先递归再交换,结果一样。但你要能解释为什么一样——因为交换这个动作本身会影响后续递归进入的是哪棵树,如果你先交换再看子树,就相当于原先的右子树展开到了左边,整个过程是对称的。
2.5 线性动态规划与贪心:实习面试需要掌握到哪个档位
动态规划是想进大厂前端实习时绕不开的内容,但它在前端面试中的范围比想象中收敛很多。
真正高频出现的是这几类:斐波那契型递推(爬楼梯)、单次交易类型的股票买卖、最大子数组和、一维路径问题。背包问题、状态压缩 DP、区间 DP 这类复杂度更高的模型,前端实习面试出现概率很低,不建议作为主攻。
| 力扣题号 | 题名 | 主要练什么 |
|---|---|---|
| 70 | 爬楼梯 | 先递归找思路,再用数组滚动优化 |
| 118 | 杨辉三角 | 二维数组状态转移入门 |
| 53 | 最大子数组和 | 动态规划思想的“连续子数组”版 |
| 121 | 买卖股票的最佳时机 | 一次交易,记录历史最低点 |
| 122 | 买卖股票的最佳时机 II | 贪心直观解法 |
| 198 | 打家劫舍 | 线性 DP 的状态转移,选或不选 |
“爬楼梯”一定要经历过一个完整思考过程:先按斐波那契直接写递归,快速想明白会超时,因为大量子问题被重复计算。然后再自顶向下加备忘录,最后自底向上写循环。这个过程本身就是动态规划思想的完整演示。你不需要背状态转移方程式,你需要在白板上从“第 n 级只能由第 n-1 级或第 n-2 级迈上来”出发,把方程推出来。
“最大子数组和”是典型的前端友好 DP 题,它的问题描述很日常:在一个含负数的数组里,找一个连续子数组,使其和最大。状态定义可以设成“以第 i 个元素结尾的子数组的最大和”,那么转移时只需要比较“只取当前元素”和“前面的最大和 + 当前元素”两个选择。如果不告诉你状态定义,很多人会卡住,因为你不知道该记录“到目前为止最好结果”还是记录“以当前位置结尾的结果”。练这道题的价值正在于此:学会定义状态,而不是凭空套公式。
贪心在实习面试里一般只会出现很简单直接的题目。买卖股票 II 号称可以多次交易,只要明天的价格比今天高,今天买入明天卖出就能获得利润。这题用贪心的视角无比简单:把每一段价格上升的差值累加起来即可。但如果你不理解贪心为什么在这里适用,遇到变体题时又会倒回去怀疑自我。所以要做的不是背答案,而是想清楚“局部最优为什么能构成全局最优”——因为没有交易次数限制,也不存在手续费,所以不必为了“未来更高的价格”而放弃眼前的上涨区间。
2.6 排序、二分与栈队列:查缺补漏的小专题
在前端实习面试中,下面这些题不一定像前面几类出现频率那么高,但它们负责兜底。你不可能每场面试只遇到数组和二叉树。把栈、队列、二分这些基础武器也顺过一遍,面试时心态会稳很多。
| 力扣题号 | 题名 | 主要练什么 |
|---|---|---|
| 20 | 有效的括号 | 栈的经典应用,匹配问题 |
| 155 | 最小栈 | 辅助栈思维 |
| 232 | 用栈实现队列 | 两个栈的倒换 |
| 704 | 二分查找 | 二分边界,基础中的基础 |
| 215 | 数组中的第K个最大元素 | 快速选择/堆,进阶选练 |
| 88 | 合并两个有序数组 | 从后向前合并避免覆盖 |
“有效的括号”几乎每三轮前端面试就会出现一次,因为它把栈的应用写得很直白。遇到左括号入栈,遇到右括号时看栈顶是否匹配。需要额外注意的点有两个:
- 遍历结束后不能只返回
true,必须检查栈是否为空,否则"((("会被错误判定为合法。 - 匹配关系千万不要写一连串
if再嵌套一个很长的else if,建议用一个映射对象把配对角存下来,代码可读性会高很多。
第 704 题“二分查找”表面上只考一个模板,但二分最容易错的是边界条件:循环条件是 left < right 还是 left <= right,mid 更新时到底要不要 +1 还是 -1。我的建议是固定使用一套自己真正理解了的模板,而且每一次都把“搜索区间”的定义写在注释里。写代码时明确区间是左闭右开还是左闭右闭,然后全程保持一致。这样即便题目换成“搜索插入位置”“寻找峰值”,你也能分析出正确的转移方向。
3. 前端独有的“现场代码”训练法:让算法题和手写 API 相互打通
算法面试题之外,前端实习面试还有一个高度相关的修罗场:手写 API。比如手写防抖节流、深拷贝、Promise.all、模拟 new 和 bind、实现 Array.map、JSON 序列化的替代方案。如果你问我这算不算算法,我的看法是:它在思维方式上就是算法题——只是不再作用在链表和二叉树上,而是作用在 JavaScript 的运行时机制和语言特性上。
我建议把它们放进算法训练计划里,间隔着练,而不是等到面试前几天才突击。因为二者能互相促进:
- 手写
Array.map能加深对回调函数、稀疏数组、参数传递的理解,这种理解反过来帮助你在刷“数组变换”类算法题时更快写出没有 bug 的循环。 - 手写深拷贝要处理循环引用、不同数据类型,这个过程要设计“已经访问过的对象”的记录,和哈希表、图遍历中的状态记录思路完全一致。
- 手写
Promise.all必须设计并发收集、失败处理、结果顺序保持,这背后的状态机思维也适用于很多动态规划题的“阶段推进”。
举例来说,实现一个简易版深拷贝时,如果不考虑循环引用,代码很容易写成递归拷贝。可一旦对象里有 obj.self = obj,递归就会无限进行。正确做法是额外用一个 WeakMap 保存“已经拷贝过的源对象”,遇到重复引用时直接返回之前拷贝好的结果。这个技巧和力扣里“克隆图”这道题的核心解法几乎一模一样。很多前端同学把“算法”和“业务”完全割裂,实际错失了很多融会贯通的机会。
面试时的临场表现也取决于你的“现场代码”熟练度。我见过不少人能很流利地说出防抖节流的原理,但一让他写:
javascript复制function debounce(fn, delay) {
let timer = null;
return function (...args) {
if (timer) clearTimeout(timer);
timer = setTimeout(() => {
fn.apply(this, args);
}, delay);
};
}
就会卡在 this 的处理上,或者忘记返回一个新函数。这个新函数才是防抖真正暴露给外部的东西,也是一个高阶函数设计的核心:它捕获了 timer 这个闭包变量。能把这个解释清楚的人,再去刷“LRU 缓存”这类需要封装数据结构的题,思路也会更顺。
所以不妨每周给自己固定一个下午,做两到三道力扣题,再做一到两道手写 API 题。不要把两类割裂开。它们其实都是同一种能力的两个场景:理解约束、定义状态、写出严密的控制流、考虑边界和异常。
4. 复盘漏掉的两次教训:白刷与错刷之间差的可能只是方法
每个人刷题都会有一段虚假勤奋期,我也不例外。这里说两个失败的复盘案例,你看了也许能躲开。
第一次教训是“按题号主线以外的热榜去刷,反而把自己心态刷崩了”。当时我学到动态规划后劲头很足,直接点开力扣的困难题排行榜,想挑战自己。那道题要求用线段树维护区间最值。我硬啃了两个晚上也没有完全吃透,反倒把树上递归的底子搞得不自信了。事后回过头来发现,那道题在正常前端实习面试中几乎不会出现。我把大量时间投入到了超过目标难度的方向,却影响了基础题的熟练度。
第二次教训是“写题时总觉得不该看题解,死磕到底才算本事”。这个观念也要分情况。如果你给一道题定了 15 到 20 分钟,依然没有任何思路,说明这道题大概率涉及你不熟悉的知识模型,此时最有效的做法不是继续耗,而是去读题解,把核心思路弄懂,再关掉题解自己完整重写一遍。把题解变成自己的思考素材,比坐在那里和自己的挫败感搏斗有价值得多。
复盘之后我给自己定了刷题节奏,这个节奏后来成了我推荐给其他人的模板:
- 一轮:按这篇文章的分类线性刷。每道题先独立思考 15 分钟,超过 15 分钟没有思路就打开题解看思路,但不要照着抄代码。看明白以后,关掉所有参考,自己重写一遍。
- 二轮:只看题号或题名,不看题目详情,直接说思路和复杂度。说不出来的标记为“需要复习”,第二天再做一次。
- 三轮:把重点题目的变形问法总结出来。比如看到“找两数之和你就想到哈希表,看到原地数组变换就想到双指针”,形成条件反射。
这种“三轮复习”的思路不是新的,但我发现很多前端同学准备算法时只走第一轮,从来不复习、不让思路过期。要知道实习面试常考的核心题目数量并没有多到离谱,你与其刷 200 道题做一遍,不如把 60 道核心题做三遍,对面试表现的帮助更大。
我还想再讲一个“把题目讲给别人听”的训练方法。每次做完一道有代表性的题,你可以打开录音或找个朋友,用 1 分钟说出:
- 题目要求做什么;
- 我打算用什么数据结构、什么思路;
- 复杂度是多少,为什么;
- 有哪些容易忽略的边界。
这个过程会强制你从“敲代码模式”切换到“讲题模式”。很多同学代码写得没错,但一被面试官问“能讲讲你的思路吗”,就只会复述代码,欠缺抽象概括能力。而面试时考察的往往恰恰是后者。我自己练了一周后,最大的变化是即使算法题没写完美,也能通过讲清楚思考路径让面试官看到潜力。
5. 扎实通过前端的算法面试:几个把题做“稳”的小习惯
最后分享几个我在实习面试临近前反复提醒自己的习惯。它们没法替代刷题量,但能帮助你在一道题上的真实表现超过刷题量本身。
第一,开头先把输入范围和极端情况看清楚。如果题目给的是数组,先问自己:数组为空怎么办?数组长度是 1 怎么办?元素可能是负数吗?能保证都是整数吗?把这些写在代码注释或直接作为前置判断放进代码里,会大幅减少低级 bug。这个习惯手写前端业务函数时更是保命利器——很多线上 bug 就是「输入没按预期来」造成的。
第二,写码前先给核心变量一个清晰命名,并说明它的含义。比如双指针里通常一个叫 slow 一个叫 fast,或者一个叫 left 一个叫 right。命名清晰不仅让面试官容易看懂,也会让代码在复杂时让你自己少绕弯。前端业务里密密麻麻的 data、res、temp 已经够让人头疼了,面试时请别再继续使用这种命名。
第三,写完以后主动测试一段小用例。哪怕是偷懒只测一个例子,也要走一遍循环。这个动作向面试官传递的信号是:我知道验证很重要,我会为自己的代码负责。这个意识在实习工作中极其宝贵。大多数崩溃发生在“边界、空值、重复值、超大值”上,你如果能主动覆盖,会很加分。
第四,复杂度的表达要习惯成自然。前端虽然不像后端那么常聊性能,但“这个解法是 O(n) 遍历,额外空间是 O(n)”这类表述仍然是基本素养。有一次面试,我的代码在思路上并没有最优,但我先写了 O(n^2) 版本,随后主动指出可以用哈希表降复杂度,面试官就很满意。先产生一个正确但不优的方案,再提出优化思路,比一上来就憋最优解要自然得多。
第五,面对不会的题,不要慌着放弃,也不要硬编。比较好的表述是:“我先按最朴素的暴力思路处理一遍,再分析有哪些重复计算……”只要方向合理,面试官就能继续和你讨论下去。前端实习面试并不仅看最终答案,它也在看你在压力下的合作性。
把这些习惯保持到面试考场上,你刷过的力扣题会真正成为你的武器,而不是躺在提交记录里的数字。算法功底这东西,少一点了容易心虚,但准备到这个程度,应对前端实习的主流考察已经足够稳。而我个人更看重的,是你在这段刷题中沉淀下来的边界意识、复杂度直觉和把复杂问题拆清楚的能力——这些能力会在你真正开始写业务代码的那天,以你意想不到的方式回报你。
