每年8月底到10月,校招笔试就像一场大浪淘沙。我当年投商汤科技的时候,看着招聘页面上“Web前端开发工程师”几个字,第一反应是:一家人工智能公司,前端笔试应该不会太为难人吧?结果拿到2018校招第一场笔试题的瞬间,我才意识到自己错得离谱——商汤的笔试不是“前端知识摸底”,而是“拿着前端岗位的入场券,做一场算法与计算机基础的全面体检”。
这篇文章就围绕这场笔试,把我印象里的题型分布、核心考点、答题思路,以及后来复盘时总结出的备考方法完整梳理一遍。无论你是正在准备AI公司前端岗位的应届生,还是想了解大厂笔试套路的前端从业者,这篇文章都能给你一个比较清晰的参考。
1. 笔试整体概览:题型分布与时间分配
1.1 商汤的笔试风格:算法权重远超常规前端考核
先说大背景。2018年那会儿,商汤科技已经在计算机视觉领域做得风生水起,核心业务是深度学习算法、人脸识别、图像处理这些方向。你想想,一家技术驱动型的AI公司,招人时最看重的是什么?是逻辑思维和算法功底。前端岗位虽然不直接写神经网络,但整条技术线都建立在“计算思维”之上,所以笔试不可能只考页面布局和框架API。
当时这场笔试的题型结构,大致是:
| 题型 | 题量 | 分值占比 | 考察方向 |
|---|---|---|---|
| 单选题 | 15题左右 | 约30% | JS基础、网络协议、浏览器原理、数据结构 |
| 多选题 | 5题左右 | 约15% | 边界情况辨析、多选少选均扣分 |
| 编程题 | 2-3题 | 约40% | 算法实现、字符串处理、动态规划 |
| 简答/设计题 | 1-2题 | 约15% | 前端工程化方案、场景设计 |
整个笔试时长我记得是90分钟。线上笔试系统用的牛客网,编程题需要自己处理标准输入输出,这一点和LeetCode那种只需要写函数的习惯完全不同,很多人第一次接触会非常不适应。
1.2 时间安排与答题节奏:先易后难才是正确策略
我那次笔试踩过最大的坑,就是在一道字符串处理的编程题上死磕了快40分钟,导致后面两道更简单的题没时间写。后来复盘才总结出正确的答题节奏:
- 前20分钟:快速过完所有选择题,会的立刻选,拿不准的做个标记。多选务必谨慎,不确定的选项宁可不勾,因为多选是倒扣分制。
- 中间40分钟:集中精力做编程题,先读一遍三道题,按难度排序,从最简单的做起。每道题控制在15分钟以内,超过20分钟还没有思路就先跳过。
- 最后20分钟:回头处理标记过的选择题,再给自己留5分钟检查编程题的输入输出格式。
这个节奏的核心思路是“利益最大化”。笔试不是高考,不需要每道题都答完,关键在于把能拿到的分稳稳拿到手。商汤这类AI公司笔试的容错率其实不低,因为题目整体偏难,你只要比同批次的人多对一两道,排名就能往前走很多。
2. 算法与计算机基础:AI公司的“隐形门槛”
2.1 数据结构和算法题的高频考点
商汤笔试的编程题和选择题,对数据结构的考察非常直接。我记得当时印象比较深的知识点有这么几类:
- 字符串处理:字符去重、最长无重复子串、模式匹配。这类题在图像领域有天然的应用联想——图像标注、文本识别都要处理大量序列数据。
- 数组与矩阵:二维数组的旋转、矩阵搜索、滑动窗口。我当时考到一道类似“有序矩阵中查找目标值”的题,本质上就是LeetCode 240的变体,思路完全一致,从右上角开始走。
- 动态规划:经典背包问题、最长公共子序列、编辑距离。AI公司对DP的执念非常深,因为深度学习里的很多优化思想,本质上也带着“状态转移”的影子。
- 二叉树遍历:前中后序、层次遍历、最近公共祖先。选择题里必有一道,编程题偶尔会出现。
让我印象最深的是,选择题有一道考察“数组和链表在插入、删除、随机访问时的时间复杂度对比”,这题本身不难,但它放在商汤的卷子里就很有意味——图像数据动辄就是几万几十万维的矩阵,选错数据结构,性能差距是数量级的。前端虽然平时操作DOM居多,但理解底层数据结构,是区分“会用JS”和“懂计算机”的分水岭。
2.2 为什么AI公司前端也要考算法:专业关联与选人逻辑
很多前端求职者看到算法题就头大,觉得“我一个写页面的,为什么要做动态规划?”说实话,我以前也有这种想法,但后来真正工作中接触了Canvas渲染、图像处理、大数据量前端可视化之后,才明白商汤这类公司考算法的逻辑在哪里。
第一,算法能力是“筛选信号”。前端框架、UI组件这些东西,上手快的人很多,但算法基础扎实的人不多。校招没法短时间考察你的项目深度,用算法题来筛人是最公平也最有效率的手段。
第二,AI公司的前端场景确实特殊。商汤做的很多产品,比如人脸可视化、图像标注工具、视频监控大屏,本质上都是“数据密集型前端”。你写一个图像标注框,可能要在前端处理几万条坐标数据;你做一个实时人脸检测的Web端Demo,要考虑Canvas逐帧渲染的性能优化。这些场景对时间复杂度的敏感度,远超普通管理后台。
第三,算法训练出来的思维方式,决定了你写代码的“下限”。一个习惯用嵌套三层for循环解决问题的人,和一个会下意识用哈希表换时间的人,在业务复杂度上来之后,产出的代码质量会有明显差距。
2.3 一道典型题目的完整求解过程
当时编程题里有一道印象很深的题,大意是:
给定一个字符串,请你找出其中不含有重复字符的最长子串的长度。
这题在LeetCode上是第3题,属于“笔试常青树”。我当时的解法是滑动窗口加哈希表:
function lengthOfLongestSubstring(s) { let map = new Map(); let maxLen = 0; let left = 0; for (let i = 0; i < s.length; i++) { if (map.has(s[i])) { left = Math.max(left, map.get(s[i]) + 1); } map.set(s[i], i); maxLen = Math.max(maxLen, i - left + 1); } return maxLen; }核心思路其实不难:维护一个窗口,窗口内没有重复字符。当遇到重复字符时,直接把左边界跳到上次出现位置的下一个位置。用Map存储每个字符最近一次出现的索引,这样就能以O(n)的时间复杂度完成遍历。
这道题考察的点很综合:哈希表的使用、滑动窗口的思路、边界条件的处理。我第一反应是暴力解法(双重循环),但如果真写上去,遇到长字符串测试用例必超时。后来复盘才发现,这道题考察的不是你“能不能解出来”,而是你“能不能用最优解解出来”。
3. 前端专业知识:核心考点拆解
3.1 CSS布局与浏览器渲染机制
商汤笔试的选择题里,CSS部分占比虽然不算高,但考察得非常精准,几乎都在“渲染性能”和“布局细节”上做文章。
比较典型的有这么几个方向:
- 盒模型与BFC(块级格式化上下文)。题目会给你一段CSS,问你某个元素最终的实际宽度是多少。这种题绕不开box-sizing,也绕不开margin collapsing。比如两个相邻兄弟元素,一个margin-bottom: 30px,一个margin-top: 20px,最终间距是多少?答案是30px,因为垂直方向的margin会发生合并,取最大值。这个知识点不复杂,但极容易踩坑,因为现在大多数人开发都在用Flexbox或Grid,很少手写传统布局。
- 重排和重绘的触发条件。比如:修改了某个元素的width,会触发什么?修改了background-color呢?前者会触发重排(layout),后者只会触发重绘(paint)。在商汤这种做高交互可视化产品的公司,前端对渲染性能的关注度是写进笔试里的。还有一道题我记得很清楚:用transform做动画和用left/top做动画,哪个性能更好?答案是transform,因为它只触发合成(composite),不会触发重排重绘。
- CSS选择器优先级计算。这个算是老生常谈了,但笔试时不能只看“id > class > tag”这种口诀,而是要会算:内联样式权重1000,ID选择器权重100,类选择器/属性选择器/伪类权重10,元素选择器/伪元素权重1。组合起来相加比较。我当年就遇到一道题,问的是
div#app .content .item:hover这个选择器的总权重是多少,答案是100 + 10 + 10 + 10 = 130,虽然实际考的是理解,但算还是要会算的。
3.2 JavaScript语言机制:闭包、事件循环与异步编程
JavaScript部分是整个笔试的重头戏,也是最容易拉开差距的地方。商汤的JS选择题出得很有水平,经常是“给你一段代码,问输出什么”,考的都是语言底层机制。
闭包是必考的。我当时遇到一道题,大意是:
for (var i = 0; i < 5; i++) { setTimeout(function() { console.log(i); }, 100); }问输出结果。答案是输出5个5,而不是0、1、2、3、4。原因很简单:var声明的变量是函数作用域,循环结束后i已经变成了5,setTimeout回调执行时读取的是同一个i。解决办法有两个,一个是把var改成let,利用块级作用域;另一个是用立即执行函数创建一个闭包来保存每次循环的i值。
事件循环机制也是商汤笔试的高频考点。题目通常会混合考察setTimeout、Promise、async/await的执行顺序。比如让你判断一段同时包含setTimeout和Promise.resolve()的代码的输出顺序,核心规则是:同步代码先执行,宏任务(setTimeout)和微任务(Promise.then)的队列要分清,微任务在宏任务之前执行。
我当时笔试时对async/await的理解还不够透彻,差点在这类题上栽跟头。这里有一个很实用的判断方法:把async函数当成“Promise的语法糖”来看,await后面跟的代码会“暂停”到微任务队列里。比如:
async function test() { console.log(1); await Promise.resolve(); console.log(2); } test(); console.log(3);输出顺序是1、3、2。因为await后面的console.log(2)是微任务,要等同步代码执行完才执行。
3.3 工程化与浏览器缓存:笔试中的“隐藏分”
商汤笔试里还有一个容易被忽略的板块是前端工程化和浏览器知识。这部分题目不多,但胜在稳定,而且答对了非常能体现一个人的工程素养。
工程化相关的考点集中在webpack上。比如:webpack的构建流程是什么?loader和plugin有什么区别?前者是让webpack能够处理非JS文件,后者是扩展webpack本身的功能。还有一道题问:“开发模式下,修改代码后浏览器自动刷新,依赖webpack的哪个能力?”答案是HMR(热模块替换),它能在不刷新整个页面的情况下,只替换修改过的模块。
浏览器缓存机制也出现过。我印象最深的是一道关于HTTP缓存头的题:Cache-Control: max-age=3600和ETag分别属于强缓存还是协商缓存?前者是强缓存,后者是协商缓存。还有一道题问,当强缓存和协商缓存同时存在时,浏览器先检查哪一个?正确答案是先检查强缓存,如果强缓存失效,再携带ETag等标识发起请求,走协商缓存逻辑。
这些知识点单独看都不难,但放在一张90分钟的卷子里,配合算法题和编程题一起考,就特别考验平时的积累。很多人临时抱佛脚是背不完的,只有真正写过项目、用过webpack、配置过缓存的人,才能又快又准地答出来。
4. 笔试题型复盘与应对策略
4.1 选择题的常见陷阱:多选少选扣分最致命
商汤笔试的多选题是真的坑,不是“选对几个给几分”,而是“多选、错选、少选都不得分”。这意味着你遇到不确定的选项时,最理性的策略是“宁缺毋滥”。
我自己的体会是,简单题选项一定是对的,但容易在“边界情况”上设陷阱。举个例子,有一道多选题问哪些JS方法会改变原数组,选项里有push、map、filter、splice。很多人一看“map和filter是纯函数,不改变原数组”,就选了push和splice,这个没问题。但当选项里加入sort和reverse时,就容易翻车——sort是无参调用时按字典序排序的,很多人不知道默认排序会把10排在2前面。
再比如考察数组去重的方法,可能会同时出现Set、filter + indexOf、reduce、includes这几种解法,然后问你哪些能正确处理“NaN”和“对象”的去重。实际里面Set是可以处理NaN的(因为NaN === NaN在Set里被视为成立),而indexOf方案处理不了NaN(indexOf内部是严格相等比较,找到NaN会返回-1)。
遇到这类题,我建议把每个选项当成判断题来做:先不看其他选项,独立判断这个选项对不对,最后再综合排查。千万不要因为某个选项看起来“眼熟”就勾选,眼熟不等于正确。
4.2 编程题的高效解题思路:从复杂到简单拆解
编程题是商汤笔试最大的分值来源,也是最不确定的部分。我根据自己的笔试经验和后来的刷题复盘,总结出一套比较高效的解题流程:
第一步,先花1分钟读懂题目,圈出输入范围和边界条件。比如输入数组的长度上限是10^5,那双重循环的方案基本要放弃;如果题目的数据量很小(不超过100),那暴力解法反而最快、最不容易出错。
第二步,想清楚暴力解法怎么写。这一步不是让你直接写,而是帮助你理解题目的逻辑。暴力解法也是最容易验证正确性的解法,一旦你确认了暴力解法的逻辑是对的,再去想优化方案,思路会清晰很多。
第三步,找时间复杂度瓶颈,思考用什么数据结构或算法思想来优化。常见的优化思路有:用哈希表把O(n)的查找变成O(1);用双指针把O(n^2)的遍历变成O(n);用动态规划把指数级的暴力搜索变成多项式复杂度。遇到字符串匹配,想到KMP或哈希;遇到区间问题,想到排序+扫描线;遇到树结构,想到递归或栈模拟。
第四步,写代码前先画出数据流。比如输入是一个字符串,中间处理成什么结构,最后输出什么格式。笔试的在线判题系统对输出格式极其严格,多一个空格都可能导致AC变成WA。
第五步,务必自己构造几个测试用例,尤其是边界用例。空字符串、数组只有一个元素、所有元素都相同、最大值和最小值相邻……这些边界情况往往是隐藏扣分点。我笔试时很多题目提交后显示答案错误,回头发现不是逻辑问题,而是没有考虑输入为空的情况。
4.3 简答题如何答出区分度:结构比字数重要
商汤笔试最后一般会有一两道简答或设计题,分值不高,但非常能体现一个人的专业深度。我当时遇到的大概是:“如何优化一个首屏加载速度很慢的Web应用?请给出你的优化方案。”
这种题没有标准答案,考察的是你的知识面和分析问题的框架。我给出的方案,后来在复盘时觉得比较能得分的结构是:
- 先定性:首屏慢的核心问题,要么是资源体积过大,要么是请求数量过多,要么是渲染阻塞。
- 再给出分层优化:网络层开CDN、启用Gzip压缩、配置HTTP缓存;构建层做代码分割和懒加载,把首屏用不到的代码拆出去;渲染层对关键的CSS做内联,JS脚本加上async/defer避免阻塞解析。
- 最后给出验证手段:用Lighthouse跑分,查看First Contentful Paint和Largest Contentful Paint两个指标,对比优化前后的数据变化。
这个回答的核心是“先诊断后下药”,你既没有只说概念,也没有只列具体手段,而是展示了一套完整的分析思路。对AI公司来说,他们要的就是这种有逻辑、有层次感的问题解决能力。
5. 备考路径与实用经验
5.1 针对AI公司前端岗的差异化备考:算法和工程化两手抓
如果你也想投商汤或者其他AI公司的前端岗位,我建议你提前两周做针对性准备,而不是漫无目的地刷题。
第一,算法刷题一定要有取舍。时间有限的情况下,优先刷高频题,比如:数组和字符串处理、滑动窗口、双指针、哈希表、二叉树遍历、动态规划入门题。LeetCode的热门100题和剑指Offer是必刷的,而且要把每题的时间复杂度都分析清楚。刷题的时候不要只看题解,一定要自己动手写在纸上或编辑器里,模拟笔试的手写环境。
第二,前端基础要按“渲染性能”这个主线去准备。AI公司的前端产品大多是数据密集型、交互密集型的,所以对浏览器渲染机制、重排重绘、requestAnimationFrame、Canvas性能优化这些知识点的考察概率非常高。建议你花一个周五下午,把“浏览器渲染流水线”从输入URL到页面显示的全过程梳理一遍,中间所有的缓存、解析、布局、绘制环节都吃透,这是性价比极高的复习内容。
第三,工程化能力要能拿得出手。不要只刷题不写项目,商汤这样的公司还是挺看重候选人的手写代码能力和工程意识的。可以自己试试从零搭建一个React或Vue的项目脚手架,配好webpack或Vite,做一下代码分割和懒加载,部署到服务器上。你真正动手做过一遍,和处理一道工程化简答题时的底气是完全不同的。
5.2 考前24小时与考场心态:真实场景下的最后冲刺
笔试前的最后一天,不建议再做新题了,尤其是夜深人静时千万别刷难题,否则第二天会带着挫败感进考场。我当时考前的安排是:
- 上午:复习自己刷过的错题,尤其是选择题里反复错的JS概念题,比如this指向、原型链、事件循环。
- 下午:用牛客网模拟一次完整的笔试流程,注意掐时间,练习标准输入输出的处理方式。
- 晚上:把浏览器渲染、HTTP缓存、事件循环这三块的知识点用思维导图过一遍,快速恢复记忆框架。
到了考场里,有几个小技巧很实用。第一,打开试卷后先花3分钟通读全部题目,对整个难度分布心里有数。第二,编程题如果是牛客网这种平台,记得先写一个读取输入的模板,比如readline或process.stdin,提前调通再开始写核心逻辑。第三,遇到卡壳的题果断跳过,笔试拼的不是单题满分,而是整体排名。
还有一个容易被忽视的点:保持开放的心态。很多人在笔试时看到题目不熟悉就开始慌,其实在校招场景下,你做不出来别人也大概率做不出来。商汤这种公司的笔试,考察的不是“满分选手”,而是“在压力下仍然能保持清晰思路”的人。你只要保证会做的题不丢分、不会做的题不空着(选择题可以按概率猜,编程题至少写出暴力解),结果一定不会差。
6. 常见问题与复盘建议
6.1 复习时最容易踩的坑
回顾我自己和身边同学准备商汤笔试的过程,有四个最常见的坑值得单独拿出来说。
第一个坑是只刷题不总结。很多人刷题刷得飞快,一天能做十几道,但问他对某类题有什么通用解法时却说不出来。刷题的价值在于提炼出套路,比如“看到有序数组找目标值,大概率是二分查找的变体”“看到子串、子数组这类关键词,优先思考滑动窗口”。每一道错题,都应该在题解旁边用一句话总结出这题考了什么知识点、用了什么解法、有哪些边界情况。考前翻这个“一句话总结本”,比重新刷一遍题效率高得多。
第二个坑是忽视笔试题环境和手写代码。LeetCode刷习惯了自动补全,到了牛客网笔试反而不会读输入了。尤其是Node.js环境下的readline、process.stdin这些API,平时写代码根本用不到,但笔试一定会用到。建议提前牛客网上多做几套模拟题,把读入、切分、输出的模板代码背下来,到了考场直接复制粘贴改逻辑,能省很多时间。
第三个坑是不重视“网络和浏览器存储”。前端基础里CSS、JS是大家都会复习的,但网络协议和浏览器存储经常被忽略。商汤笔试选择题里出现过Cookie、localStorage、sessionStorage三者的区别,也出现过TCP三次握手到底交换了什么内容。这些知识点不难,但临时抱佛脚时容易漏掉,属于“背了就能拿分”的性价比之王。
第四个坑是情绪管理失败。笔试过程中遇到不会的题,心态崩了,后面的题目完全没心思看。我当年第一道编程题不会做,后来才知道大部分人也没做出来。反而是那些“先把会做的做了,再回头试着写暴力解”的人,往往能走到下一轮。校招笔试说到底是一个“相对竞争”的游戏,不需要绝对高分,只需要比同批次的人稳。
6.2 考完之后的复盘与机会跟进
笔试结束后千万别干等着,复盘比考试本身更重要。我自己习惯的复盘步骤是:先把所有题目凭记忆整理到一个文档里,能回忆多少写多少,尤其是编程题的题目描述和当时写的代码;然后把每道题对应的知识点列出来,去网上搜索参考答案,逐题对照;最后把错题整理进错题本,标注出错原因。
复盘最大的价值在于,你笔试时暴露出来的薄弱点,往往也是后续面试时面试官追问的重点。如果你笔试里有一道事件循环的题答错了,面试前一定要彻底搞懂这个机制,因为面试官手里是有你的笔试成绩单的,他很可能会针对性地追问你没掌握的知识点。我当年笔试时有一道关于闭包内存泄漏的题答得含糊,后来一面的时候面试官果然问了一个“闭包在什么情况下会导致内存泄漏,如何避免”的变体题。正因为提前做了复盘,才没有被问倒。
另外,商汤这类公司的招聘流程通常不止一轮,笔试过了之后还有技术面、交叉面、HR面。笔试成绩好,进入面试后会有一定优势,但也不代表万事大吉。面试环节更看重的是你项目经历里体现的真实能力。
6.3 我的个人体会:笔试不是终点,是筛选真正热爱的人
最后说点自己的感受。经历过商汤这场校招笔试之后,我有一个很深的体会:一份好的前端笔试题,从来不是为了难倒你,而是为了筛选出真正热爱计算机基础、愿意深入钻研的人。商汤把算法、数据结构、浏览器原理、工程化全部糅合在一张卷子里,并不是要你每道题都会,而是想看看你在有限的时间里如何做取舍、如何应对不确定性、如何在压力下保持思路清晰。这些能力,恰恰是工作中解决问题最需要的底层素养。
如果你正在准备类似的校招笔试,请相信:刷过的每一道题、总结过的每一个知识点、写废的每一版代码,都会在考场上变成你的底气。这场笔试的结果固然重要,但准备过程中你建立起来的知识体系和思维框架,才是真正伴随你走得更远的东西。