2018年秋天,我在宿舍点开蘑菇街的实习生笔试链接时,满脑子都是LeetCode。做完两道编程题之后发现,真正让人冒汗的不是手写快排,而是一道关于直播间商品点击率预估的开放题。这份被后人翻出来的“蘑菇街2019届实习生-算法工程师笔试试题”,表面上是一份卷子,实际上是一份电商算法岗的能力地图。如果你正准备投电商公司的算法实习,或者想检验自己的知识体系有没有缺口,这篇文章值得完整读完。我会从题型分布、典型题目拆解、业务场景答题思路、考场策略四个维度,把我后来做面试官时同样会考察的东西一次讲透。
1. 从一份笔试标题能读出什么:蘑菇街到底想招什么样的实习生
1.1 电商算法岗的“笔试”和“大学期末考试”不是一回事
蘑菇街的定位是女性时尚内容电商,核心业务里堆满了直播、穿搭社区、商品推荐、营销活动这些东西。可想而知,它的算法团队日常在做的事,基本绕不开推荐、搜索排序、商品理解、直播内容理解、用户增长和风控。实习生笔试不是学校里的“数据结构期末考试”,不是把红黑树旋转写对就能拿高分,而是公司用来做海选过滤的一道闸门:在两个小时左右的时间里,判断一个人能不能干算法工程师的活。
“干算法工程师的活”这个标准,听起来虚,落到笔试里其实很具体。第一,编码得干净,边界条件不能漏;第二,算法基础得扎实,不能只会调包;第三,机器学习基础概念要能写推导,不能只背名词;第四,遇到业务开放题,得能结构化地拆解问题,而不是天马行空乱写。这四点里,前三点是硬功夫,第四点是区分度所在。蘑菇街这种电商公司尤其看重第四点,因为它所有的算法最终都要落到“商品转化率提升”“直播曝光利用率提升”这些业务指标上。
所以你看这份笔试,虽然挂着“算法工程师”的名字,但它的底层逻辑和纯互联网公司、纯研究型岗位都有点不一样。它更像是在问:你能不能把学校里学的模型,变成一套能服务几千万用户、几百万商品的系统里真正跑起来的东西。看懂这一层,就不会再抱着“把LeetCode刷三遍就能过笔试”的心态去准备了。
1.2 为什么总有同学觉得题目“偏”
每年笔试结束,都有同学在网上吐槽:说好的算法岗,怎么考了这么多业务题?为什么LR的损失函数推导也写不够好?为什么给了一道“直播间冷启动怎么做”的开放题,我完全不知道从哪下手?
其实不是题目偏,是很多人对“算法工程师”的理解太窄了。在学校做实验,数据集是干净的,评价指标是固定的,模型跑完就能交差。但电商算法面对的是:样本分布一天一个样,正负样本比例可能到1:99,线上效果和离线指标经常不一致。笔试里出现“样本不均衡怎么处理”“冷启动怎么做”“如何设计一个召回策略”这类问题,就是在提前筛选那些具备工程直觉和业务 sense 的人。
我后来参与过校招和实习生的笔面试出题,发现出题人有一个共识:不在乎你背了多少个模型名字,而在乎你面对一个真实问题的时候,能不能快速划出边界、给出可行方案、说清楚优劣取舍。这份“蘑菇街2019届实习生-算法工程师笔试试题”之所以到今天还被人翻出来讨论,就是因为它把这种思路体现得很典型——通用算法题保底,机器学习基础题拉开差距,业务开放题决定上限。
2. 编程题:这些代码题怎么从“通过”变成“加分”
2.1 常考的数据结构与算法题型分布
电商算法实习笔试的编程题,一般不会出特别偏门的算法,但也绝不是LeetCode Easy难度就能应付的。从题型分布来看,出现频率最高的是这几类:
| 题型 | 常见问法 | 建议最低掌握级别 |
|---|---|---|
| 二分查找 | 有序数组找目标、找旋转点、找边界 | 手写不 bug 的二分 |
| 链表 | 反转、找环、合并有序链表 | 指针操作熟练 |
| 二叉树 | 遍历、最近公共祖先、层序 | 递归和迭代双写 |
| 动态规划 | 背包、子序列、编辑距离 | 状态定义清晰 |
| 排序与Top K | 第K大、前K个高频元素 | 至少掌握堆和快排 partition |
| 字符串 | 最长子串、匹配、滑动窗口 | 滑动窗口模板 |
如果你是2019年那一届,当时的氛围普遍还停留在“刷题量决定一切”。但我后来复盘这套笔试时会发现,它考的编程题并不追求偏怪难,而是特别看重“在紧张状态下能不能把一道中等难度的题写对”。尤其是Top K、二分、DP这些话题,几乎是必考。
一个特别容易被忽视的方向是时间复杂度分析。很多同学能写出解法,但被问“你这个复杂度是多少?能不能再优化”就卡住。笔试不一定像面试那样问,但你写出的代码复杂度过高,在线判题系统会直接告诉你超时。所以准备阶段就得养成习惯:每做完一道题,顺手把时间复杂度和空间复杂度写出来,对照题目给的数据范围算算会不会爆。
2.2 一道“Top K”题目的四层解法递进
有一类题目在电商算法笔试里出现率极高:给定一个长度为 n 的整数数组,返回第 K 大的数。它几乎是把“基础算法能力”和“工程优化意识”一起考了,而且解法每上一层,都对应着不同的思维层次。
第一层,直接排序,然后取下标 n-K。代码最短,但时间复杂度 O(n log n)。如果 n 是百万级,在笔试环境里可能勉强能过,但不是一个算法工程师该止步的地方。
第二层,用小顶堆维护当前最大的 K 个数。遍历数组时,如果堆的大小小于 K,直接入堆;如果当前元素比堆顶大,就替换堆顶。时间复杂度 O(n log K),空间 O(K)。这层解法已经在线性对数级别了,而且在内存受限的场景下,比如在海量数据里找 Top K,堆是工程上真正常用的方案。
第三层,快排 partition 的思想。每次选一个基准,把数组分成“大于基准”和“小于基准”两部分,然后根据基准的位置,决定继续在哪一侧搜索。这里有一个关键细节:要求第 K 大,分区时按从大到小排,还是按从小到大排,容易搞混。我的建议是先把“从小到大排”的 partition 写熟,然后统一用下标转换:第 K 大等价于排好序后下标为n-K的元素。下面是一个按从小到大版 partition 实现的写法:
import random def find_kth_largest(nums, k): n = len(nums) target = n - k # 升序排后第 K 大元素所在下标 def partition(left, right): pivot_idx = random.randint(left, right) nums[pivot_idx], nums[right] = nums[right], nums[pivot_idx] pivot = nums[right] i = left for j in range(left, right): if nums[j] <= pivot: nums[i], nums[j] = nums[j], nums[i] i += 1 nums[i], nums[right] = nums[right], nums[i] return i left, right = 0, n - 1 while True: pos = partition(left, right) if pos == target: return nums[pos] elif pos < target: left = pos + 1 else: right = pos - 1第四层,是把它改成确定性 O(n) 的 BFPRT(中位数的中位数),但笔试时一般不建议写,因为代码量大、常数大,容易写得手忙脚乱。真正在笔试里拿到高分的写法,是堆或者随机快排 partition,然后准确说出复杂度。如果还想再加一点“工程感”,可以补一句:如果内存足够,分布式场景可以先用 Hash 分桶,再在每个桶内并行算 Top K,最后归并。这句话未必会被判题系统验证,但阅卷人看到会知道你不是只会背题。
2.3 代码风格和边界条件才是分水岭
很多同学觉得编程题只要最后样例跑过就行,其实阅卷时除了看 AC,还会看代码风格。尤其实习生的代码,面试官会想象“三个月后他要进组跟我们一起写代码”,代码乱不乱,直接影响到这个想象是否成立。
我自己在笔试踩过几个坑,第一个是变量名全是 i、j、k,回头检查代码时根本不知道哪个指什么;第二个是完全没有边界条件处理,比如数组为空、K 越界、输入有负数;第三个是一上来就写复杂解法,写到一半卡住,最后连暴力解都没提交。
聊了这么多届笔试,我的建议是:小规模数据先把暴力解写出来,保证能得分,再在暴力解基础上优化。过程中把边界条件写在最前面,比如if not nums or k <= 0 or k > len(nums): return -1这种防御式写法,既不会扣分,还能展示代码习惯。毕竟笔试是限时博弈,最亏的不是不会做,而是明明会做,却因为“差一点点”没跑通。
3. 机器学习理论题:没背熟这些基础概念,笔试直接翻车
3.1 过拟合、正则化与偏差方差:必考,但不只是背概念
蘑菇街这类电商公司的算法笔试,机器学习题通常不会只问“什么是过拟合”,而是会给出一个具体场景:点击率预估模型在训练集上 AUC 0.85,验证集上 AUC 0.72,问可能是什么原因,怎么定位,怎么缓解。这种题目表面考概念,实际考的是排查能力。
我从阅卷角度说几个常见的失分点。第一,只答“增加数据量”“加正则”,没有解释为什么。第二,不知道 L1 和 L2 的本质区别,只会说“L1 稀疏,L2 平滑”。如果能补上目标函数,观感完全不一样。以线性模型为例,L2 正则在优化目标里加的是 λ∑w_j²,L1 加的是 λ∑|w_j|。因为 L1 在零点不可导,且梯度方向恒为 ±λ,所以容易把不重要的特征权重压成 0;L2 的梯度是 2λw_j,权重越小梯度越小,最后会得到一堆绝对值很小、但不为 0 的权重。在电商稀疏特征场景下,L1 能让特征维度变小、节省线上存储,L2 更稳、但特征全保留,两者各有各的用途。
另外,偏差和方差那个经典图,笔试不会让你画,但会用选择题问:模型在训练集误差很小,测试集误差大,属于高方差还是高偏差?答案是高方差,对应过拟合。这类送分题一旦丢分,会非常伤。因为它的背后是考官默认“这是基本功”,你不是不会,而是没复习到。
3.2 排序模型:从LR到FM再到深度学习
电商算法的一个核心任务是排序,所以和排序模型相关的题目也经常出现。常见问法包括:LR 怎么做 CTR 预估?FM 相对 LR 解决了什么问题?DeepFM 为什么有效?
先说 LR。逻辑回归做点击率预估,最基础的三板斧是:特征离散化、特征交叉、在线学习。笔试如果考推导,通常会让你写出预测函数和损失函数,然后求梯度。预测函数记作:
- h(x) = 1 / (1 + exp(-w^T x))
- 损失函数是交叉熵:Loss = -1/m * Σ [ y log h(x) + (1-y) log(1-h(x)) ]
- 对 w_j 求偏导,结果:∂Loss / ∂w_j = 1/m * Σ (h(x) - y) * x_j
这个“预测值减真实值,再乘特征”的梯度形式,是 LR 在广告和推荐领域被大规模使用的重要原因之一:计算简单,更新快,特别适合海量稀疏特征。能把这个推导写下来的同学,在笔试里会比只会“import LogisticRegression”的同学高一个档次。
FM 那个知识点也很典型。LR 本身不能自动学习特征交叉,需要人工做二阶特征组合。FM 引入隐向量,让每个特征学一个 k 维向量,二阶交叉项的权重用两个向量的内积来表示,这样就可以在稀疏数据下学到可靠的交叉权重。这个点经常以选择题出现:FM 的时间复杂度是多少?如果是朴素实现是 O(n^2 k),但通过数学变换可以优化到 O(nk)。能把这个问题讲明白的,说明不是只看了名字。
再往后就是深度学习模型,从 Wide & Deep 到 DeepFM、DIN,问法通常是“侧边栏 Wide 部分的作用是什么”“为什么引入注意力机制”。这类题不用写公式,但要能说出设计动机:Wide 管记忆,Deep 管泛化;DIN 管用户历史行为和当前候选商品的相关性。答题时记住一个原则:先说模型解决什么问题,再说结构怎么设计,最后说指标提升或业务收益。这就是标准的“算法答题三段式”。
3.3 样本不均衡与评估指标:电商场景最爱问的问题
电商场景里,点击率、购买率、复购率这些目标,天然是正样本极少的。一份样本不均衡考题,几乎必然会出现在笔试中。
处理手段要能说全:第一,数据层可以欠采样、过采样、合成样本;第二,算法层可以修改损失函数,比如给正样本更高的权重,或者用 Focal Loss 让模型关注难样本;第三,后处理层可以调节阈值,不一定要卡在 0.5。很多同学漏掉“阈值调节”这一步,其实这恰恰是工程里最常做、也最有效的手段。
评估指标的题目也很有迷惑性。问“用什么指标评估点击率模型”时,如果回答 Accuracy,基本就凉了,因为负样本占绝大多数,模型全部预测为负,Accuracy 也可能很高。CTR 场景通常看 AUC、LogLoss,或者分用户维度算 GAUC。更进一步,如果能说出来“AUC 只关心相对排序,不关心校准程度;LogLoss 关心预测概率是否校准”,那这个题基本就是满分。这种细节,教科书写得少,但线上模型迭代时天天都要用。
3.4 特征工程题举例
有些笔试会把特征工程专门拎出来,比如给你一个任务:预测用户未来 7 天内是否会产生复购。请你列出候选特征。这种题没有标准答案,但答得有没有条理,一眼就能看出来。
我建议按四类来答,亲测有效:
- 用户静态特征:性别、年龄档位、会员等级、注册时长。
- 商品特征:商品类目、价格带、折扣力度、产地、品牌偏好强度。
- 行为序列特征:最近一次购买距今间隔、7 天内浏览/加购/收藏次数、点击商品的价格均值、历史复购周期。
- 上下文特征:当天星期几、是否大促期间、当前渠道、城市等级。
最后再补一句:连续特征要做归一化或分桶,类别特征要做成 embedding 或 one-hot,特征构造完之后要做离线验证。哪怕笔试只要求列特征,也要把“怎么用这些特征”的逻辑带上,这能证明你不只是会列变量,而是真的会建模。
4. 业务场景题:当算法遇到直播、秒杀和“她经济”
4.1 推荐系统“召回+排序”的笔试开放式问题
业务场景题最容易出现在笔试的最后一部分,而且往往是最能拉开分数的地方。典型问法就是“请设计一个直播推荐系统”,这种题看着很大,其实是在考你脑子里有没有一套成熟的推荐系统框架。
我的答题框架是固定的:目标定义、数据层、召回层、排序层、重排层,然后再补充冷启动和评估方案。
目标定义要先说清楚,是想提升人均观看时长,还是想提升直播间成交额,因为后续所有设计都会跟着目标变。数据层要说明有哪些输入:用户信息、直播间的实时信息、用户历史行为、当前上下文。召回层写三到四种召回方式就够了:基于用户观看历史的 I2I 召回、基于相似用户的 U2I 召回、基于直播标题和标签的向量召回、热门直播间兜底。排序层写一个精排模型,比如 DeepFM 或者 DIN,说明输入特征和输出目标。重排层要提多样性和打散,避免用户看到全是同一个品类。
最后补一句“用离线 AUC 做初步验证,再上线做 AB 实验,核心指标看人均时长和成交转化率”。这套框架写下来,即便你没有真正做过推荐系统,阅卷人也会觉得你的思维已经入了门。
4.2 冷启动问题:新品、新用户、新人主播
冷启动几乎是电商算法笔试里的“必答题”。原因很简单,电商平台每天都有新用户、新商品、新直播间,没有行为数据,模型很难给出靠谱的推荐。
回答新用户冷启动,我习惯分阶段讲。第一阶段,利用注册信息做粗筛,比如年龄段、城市;第二阶段,给用户展示热门内容和高探索价值的品类,用“最热策略”保证基础体验;第三阶段,根据用户实时点击和停留反馈,快速更新候选集,用多臂老虎机这类在线探索策略,给每个候选一个置信区间,在“探索”和“利用”之间做平衡。提到汤普森采样或 UCB 时,如果能补一句“Beta 分布的参数随点击更新”,会显得非常专业。
新品和新主播冷启动的思路类似,但侧重点不同。商品没有行为数据,但商品本身有类目、价格、图片、标题,可以通过内容属性Embedding去匹配相似人群。直播间是实时内容,可以设置一个“冷启流量池”,先给一波曝光,看点击率、停留时长、互动率,超过阈值再往更大的池子里放。这就是熟知的“阶梯流量池”打法。笔试时把这个词说出来,再加上“阈值要按直播间品类差异做校准”,味道就对了。
4.3 风控与反作弊的算法思路
不是所有业务题都围绕推荐。蘑菇街这类电商平台,大促、秒杀、优惠券发放场景下,一定会有刷单、黄牛、垃圾注册这些问题,所以算法笔试偶尔也会出一道风控题,比如“如何识别刷单用户”。
最容易被接受的答案,是从规则和模型两个层面去答。规则层面:单设备注册多个账号、多账号共用收货地址、下单时间分布过于均匀、退款率异常高等,先拎出来做成黑名单特征。模型层面:把用户、设备、支付账号、收货手机号、IP 地址作为节点建图,用户和用户之间如果共享了太多关键节点,就存在聚集风险。然后可以用图算法里的连通分量、标签传播或社区发现,把“团伙”给挖出来。
风控题还有个加分点:讲清楚“准确率和误杀率”之间的取舍。宁可多放几个可疑用户,也不要误杀正常用户,尤其是大促期间,误杀直接损失 GMV。这种“业务指标优先”的思考方式,是最像真实算法工程师的地方。
4.4 如何把开放题答出高分:先说框架,再补细节
开放题最大的坑,是没有框架就开写。我看到过不少卷子,一上来就写“用深度强化学习”,然后写一堆术语,但完全没有交代输入输出,也没有评估指标,看完全篇不知道他想做什么。
我总结了一个很稳妥的答题结构,笔试时屡试不爽:
- 怎么定义问题:目标是哪个业务指标,评估指标用什么,离线还是在线。
- 数据从哪里来:有哪些特征,正负样本怎么定义,数据量大概是什么级别。
- 怎么做模型:先给一个简单基线,再给一个升级方案,分别说明收益。
- 怎么评估和上线:离线实验、AB实验、灰度放量、监控指标。
这个结构不需要长篇大论,每部分三到五行就够了,但胜在逻辑完整。开放题不是写论文,是向阅卷人展示你的思维路径。就像写代码先写注释一样,先把大框架搭起来,再往里面填细节,哪怕有些细节不一定准确,也不会整体崩盘。
5. 考场实战:时间分配、做题顺序和那些踩过的坑
5.1 题目结构与分值分布(常见的实习生算法笔试形态)
虽然不同年份、不同岗位的试卷不完全一样,但这类实习生算法笔试通常由三部分组成:选择题、编程题、问答题/开放题,整体时长在 90 到 120 分钟之间。选择题覆盖机器学习、概率统计、数据结构基础,可能 10 到 20 道;编程题通常是 2 到 3 道,需要在线运行通过;问答题一到两道,偏业务开放。
我见过最可惜的一种情况,是同学在选择题上花太多时间,结果后面编程题没做完。选择题一般 1 到 2 分一题,分值不高,但很耗时。如果你不确定某道概率题,可以先标记,最后再回来算。编程题分值高,而且只要样例通过就能拿分,优先级最高。开放题只要写就有分,但得花时间组织。
我给一个参考时间分配:选择题控制在 25 分钟内,编程题留 60 分钟,开放题留 20 分钟,剩下时间检查。当然,具体要根据题目分值灵活调整,但核心原则是:不要在低分小题上恋战,把时间留给高性价比的题目。
5.2 我的做题顺序策略
我做题喜欢“先易后难,先把保底分拿到手”的顺序。进入试卷后先把所有题扫一遍,不用逐字读,只看题型和分值。然后从最有把握的编程题开始做,或者从最容易拿分的选择题快速过一遍。
编程题如果第一道比较难,不要死磕,立刻跳到下一道。很多同学在笔试时会犯一个“不写完不罢休”的执念,结果第二道本来能拿分的题直接没时间碰。我的策略是,同一道题先写暴力解,保证样例能跑通,拿到基础分,如果时间还剩,再在当前代码基础上加优化。这和平时刷题不一样,笔试的目标是总分最大化,不是单题最优解。
另外,通读题目时,我会顺手把题目的核心条件圈出来,比如“不能使用额外空间”“第 K 大而不是第 K 小”“有序但可能包含重复元素”。这些都是容易设坑的地方,提前标记好,写代码的时候就不容易忘。
5.3 读题一半就开写是最大的坑
笔试最大的坑,不是不会做,而是“没看清题就开写”。我见过太多同学,把“返回前 K 个高频元素”看成了“按频率排序”,把“求最长不重复子串”和“求最长回文子串”搞混,写了大半天,发现完全跑偏。
这里分享一个非常笨但非常有效的习惯:写完代码后,不要急着提交,先在草稿纸上构造一个最简单的测试用例,手动推一遍输出。比如求第 K 大,就构造[1, 2, 3, 4, 5],K=2,期望输出是 4。如果代码输出 2,说明你在“升序/降序”或者“下标转换”上出了问题。这个习惯能挽救 30% 以上的低级失误。
还有一个容易被忽略的细节:输入输出格式。在线笔试平台对 Python 的input()读取方式有严格要求,如果数据是多行数字,很多人会卡在解析上。所以考前一定要去牛客网或者赛码网至少做两道完整的在线笔试模拟题,把输入解析、输出空行这些“工程细节”提前练熟。这不是算法能力问题,但的确会决定你能不能 AC。
5.4 复盘:这套笔试的参考价值
每年都有大量同学问,这些 2019 年的真题,放到现在还有没有参考价值?我的回答是:题型和工具会变,但考察底层能力的逻辑没有变。今天可能不考 LR 手推了,改为考 Transformer 或者推荐里的多目标优化,但“能否把模型原理讲清楚”“能否在业务场景里落地”“能否在压力下写出干净代码”这三件事,依然是算法实习生的核心筛选标准。
我后来参与出题时,最常说的话是:笔试本质上是给候选人一面镜子,让他们照见自己知识体系里的盲区。所以不管这套笔试你最终拿了几分,都值得做一次认真复盘。每道错题,都对应一个知识点漏洞:是概率论不熟,还是机器学习推导没练够,或者业务题没见过?把这些漏洞列出来,比单纯焦虑分数有用得多。
如果让我回到 2019 年那个晚上重新做这套题,我会在开考前做三件事:把 LR 的梯度推导在纸上默写两遍,把推荐系统的召回-排序架构背熟,再给自己规定“每题先读题 2 分钟,想清楚输入输出再动手”。很多年后我带实习生时,发现这三件事依然管用。这套笔试真正的价值,不在于你得了多少分,而在于它逼着你把学校里学的散装知识,真正装进“电商业务”这个壳里。祝你笔试顺利,也祝你在复盘时,能看见自己的成长。