ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

2020春招后端与算法工程师面经:从算法题到系统设计全复盘

2020春招后端与算法工程师面经:从算法题到系统设计全复盘

2020春招那会儿,我手里同时攥着“后端工程师”和“算法工程师”两个方向的简历,从三月中旬开始大面积投递,到四月底结束,前后刷了二十多轮面试。一个很深的体感是:无论挂的是什么岗位名字,面试官真正想验证的东西高度重合——基础是否扎实、能不能把零散知识点串成体系、遇到不会的问题时有没有推演的思路。这篇面经不是标准答案,更像是我把自己当样本,把两边的考察点、答题思路和栽过的坑一起拆开,给准备走这两条路的人做个参考。

1. 春招前先想清楚:后端岗和算法岗的考察重心根本不一样

1.1 两个岗位在面试中验证的是两种能力

后端工程师的JD通常写着“负责系统设计、接口开发、性能优化”,面试里大量时间会花在并发、存储、中间件、线上问题排查上。面试官希望你证明:把一个需求扔给你,你能不能在考虑数据一致性、可用性和扩展性的前提下,把系统真的做出来。

算法工程师的JD则更偏“建模”“特征工程”“模型优化”,面试重心会落在概率统计、机器学习基础、模型推导、业务指标分析。面试官希望你证明:给你一个业务问题,你能定义清楚目标,能设计特征与模型,还能说清楚离线评估和线上效果的差距。

这两个方向考察的能力模型有交集,比如都要会算法题,都要能讲清楚项目,但侧重点完全不同。我自己在准备阶段做了两版简历:投后端时突出中间件、数据库、高并发实践;投算法时突出特征设计、模型选型和效果指标。面试官问“你的技术主线是什么”时,最好不要含糊地说“我全都能干”,而是要明确“后端的底层知识我做扎实了,算法方向是我的加分项”这类有主次的表述。

1.2 简历上写什么,决定了你被问到什么

简历并不是项目经历越多越好,而是每条经历都要经得起深挖。我见过很多简历,写“参与开发某平台”,但面试官追问“你个人负责的部分是什么”“并发量大概多少”“遇到什么问题”时,候选人答不上来,这比不写这条经历还要减分。

我自己的做法是反向准备:简历里只留三个能讲透的项目,每个项目对应三类问题——

  • 项目背景和你做的具体事;
  • 技术选型的原因,为什么用这个不用另一个;
  • 最终效果如何量化,流量、耗时、准确率、吞吐量提升多少。

比如我写过一个“广告点击率预估服务的后端重构”,那就要准备好:老系统为什么慢、新系统用了什么结构、缓存和数据库之间怎么保证一致性、接口RT从前后的具体数值变化。面试官从这条经历里问出去的东西,基本可以覆盖大部分后端知识。

1.3 投递节奏和线上化准备

2020年春招的特殊点是大量环节改成线上视频面试,远程笔试和在线coding也变多了。这意味着更早的设备调试和网络测试很关键。我的建议是提前准备一台固定的面试设备,装好常见编码环境,提前测麦克风和摄像头,不要到了面试前十分钟才折腾。

投递策略上,我采用了“少量内推+大量海投”的组合。内推渠道能更快进入面试流程,但要选择和自己方向匹配的团队;海投平台则用来积累面试经验,哪怕是不太想去的公司,前几轮面试也可以当作免费的模拟考。我给自己定的节奏是:二月底整理知识体系,三月上旬开始投递,三月中旬到四月中旬集中面试,每周保持至少两到三场,防止手生。

2. 算法轮:高频题型、手撕代码的节奏和常见失分点

2.1 刷题数量不是核心,熟练度才是

算法题基本是后端和算法岗共同的第一关。我当时给自己定的目标不是刷完几百道题,而是把高频题型刷到“闭着眼睛能默写出框架”的程度。所谓高频,集中在:

  • 双指针与滑动窗口;
  • 动态规划;
  • 二叉树遍历与递归;
  • 链表操作;
  • 哈希表与LRU这类缓存结构。

面试现场和平时刷题最大的区别是环境压力。平时可以慢慢调试,面试时往往要在二十分钟内给出思路、实现并跑通测试。所以我把每道高频题都整理成自己的模板:比如遇到动态规划就按“定义状态—写转移方程—初始化—滚动数组优化”四步走;遇到二叉树题目就不断问自己“对当前节点做什么?要不要处理子节点结果?返回值是什么?”。

2.2 必须吃透的几类常考题型

双指针/滑动窗口。面试官常出“最长无重复子串”或“长度最小的子数组”。这类题的关键不是背代码,而是理解窗口扩张和收缩的条件。比如最长无重复子串,右指针不断向右扩展,同时用哈希表记录字符最后出现位置;遇到重复字符时,左指针跳到重复位置的下一个位置。说清楚每个指针移动的语义,代码自然就出来了。

动态规划。常见的是“零钱兑换”“最长上升子序列”“买卖股票的最佳时机”。我个人觉得动态规划最容易被追问的点是状态定义和边界条件。以“买卖股票”为例,面试时要主动写出两个维度:天数 i 和是否持有股票;转移方程是持有状态由“昨天持有”或“昨天未持有今天买入”得到;最后结果是最后一天未持有股票的最大收益。如果面试官追问空间优化,可以把二维数组压缩成两个变量。

二叉树递归。“二叉树的最近公共祖先”是高频题。递归函数的定义要明确:如果在 root 的左右子树中分别找到 p 和 q,则当前 root 就是答案;如果只在左子树找到,就返回左子树的查询结果;如果只在右子树找到,同理。不少人在写这类题时容易混淆“递归返回什么”和“当前层处理什么”,准备时要把这两个问题分开想清楚。

LRU缓存机制。这道题既是算法题,也是系统设计题。核心是 get 和 put 都要 O(1) 时间复杂度,所以要用“双向链表 + 哈希表”的结构。哈希表负责快速定位节点,双向链表负责维护访问顺序。现场写的时候要特别留意边界:链表为空时怎么处理?容量满了删头部,怎么同时更新哈希表?这些边界往往是考察代码严谨度的地方。

2.3 手撕代码时的现场节奏

我前几轮面试吃过亏:拿到题之后太想表现,立刻上手写,写到一半发现思路错了,只好擦掉重写。后来我调整成固定节奏:

  • 先复述一遍题,确认输入输出和边界约束;
  • 说一个能跑的暴力解法,再用时间空间复杂度自然引出更优解法;
  • 自己举一个示例,手动推演一遍;
  • 再开始写代码;
  • 写完先口头梳理一遍有没有空指针、数组越界、重复计算这类问题。

这套流程最大的作用是给面试官一个“你在主动思考”的信号。哪怕最终没用最优解法,沟通的过程也能留下好印象。另外,面试中如果卡住了,不要沉默超过一分钟,可以把当前想到的思路说出来,哪怕不完整,面试官通常也会给提示。

3. 后端基础轮:把零散考点串成一条知识链

3.1 操作系统与网络:从协议到系统调用

后端面试的计算机网络题,最常问的是“从输入一个URL到页面展示发生了什么”。起初我也只是背流程,后来发现面试官对每个环节都会继续追问,比如“DNS解析详细过程”“TCP为什么三次握手”“HTTP和HTTPS有什么区别”“连接释放为什么需要四次挥手”。

考点看起来散,其实是一条链:浏览器输入网址,先做DNS解析,找到服务器IP;TCP三次握手建立连接;如果走HTTPS,还要做TLS握手协商密钥;HTTP请求到达服务器后,经过反向代理、应用服务、数据库,再原路返回;最后四次挥手断开连接。每个环节背后都有设计动机。举个例子,TCP三次握手是为了同步双方初始序列号;四次挥手是因为TCP是全双工的,双方都要独立关闭各自的发送通道。把这些“为什么”讲清楚,比单纯罗列步骤更有价值。

操作系统问得比较多的还有进程与线程、虚拟内存、IO多路复用。面试官喜欢通过对比来考:多进程和多线程的优缺点、用户态和内核态切换、select/poll/epoll的区别。我在准备epoll时,用“餐厅等位”做类比:select是服务员反复去每桌问“好了没”,epoll是顾客好了主动按铃。面试中能用这种话术说明白,往往比生硬的术语更容易获得认可。

3.2 数据库:索引、事务与锁的形成闭环

数据库部分是后端面经的重头戏。我被人从“为什么用B+树做索引”一路追到“MVCC为什么能解决读写冲突”,全程都在考“原理是否真的理解”。

首先要把索引这条线理清:B+树非叶子节点不存数据、叶子节点用链表串联,天然适合范围查询和磁盘预读;哈希索引适合点查但不适合范围;覆盖索引能减少回表。接着是索引失效,面试官常给“like '%abc%'”“对索引列做函数运算”这类例子,考察你对B+树有序性的理解。

事务和锁是另一个高频池。ACID四个特性要会用例子解释;隔离级别要知道读未提交、读已提交、可重复读、串行化分别解决什么问题;MVCC通过多版本和ReadView实现非阻塞读,可重复读和读已提交的核心区别是ReadView生成时机不同;还有间隙锁、临键锁,要能说明它们怎么解决幻读。

我建议把“索引失效”“死锁”“慢查询优化”各准备一个实战案例。比如面试官问“一个查询本来几十毫秒突然变成几秒”,可以从执行计划入手,看有没有全表扫描、有没有文件排序、索引有没有被隐式类型转换破坏。这种排障思路非常加分。

3.3 缓存、消息队列与分布式问题

大厂后端面经里,Redis基本是必问项。Redis基础要掌握五种数据结构的适用场景,比如String存计数、Hash存对象、ZSet做排行榜。接着会问持久化机制,RDB和AOF的取舍;再进阶是缓存穿透、缓存击穿、缓存雪崩,以及对应的空值缓存、布隆过滤器、互斥锁、随机过期时间等方案。

我当时被追问过一个很细的问题:“缓存和数据库的一致性怎么保证?”这是没有标准答案的开放题,但我给出的框架让面试官比较满意:先更新数据库再删缓存,而不是先更新缓存;删缓存失败或延迟时,通过消息队列做最终一致性补偿;如果一致性要求极高,可以引入版本号校验。关键是要说清楚每种方案背后的延迟窗口和代价。

消息队列主要考察“为什么用”和“怎么保证可靠性”。最经典的场景是削峰填谷,比如秒杀场景把写请求先放到MQ里排队,由下游按处理能力消费。接着必问的是重复消费、消息丢失、顺序消费。我在项目里用的是“幂等表 + 唯一键约束”解决重复消费,消费前先查幂等记录,不存在则执行,存在则直接返回。这个思路放到面试里非常有说服力。

3.4 把知识点连成场景

零散背了知识点,面试官一问“你项目里为什么这么设计”还是会懵。正确的方式是把知识点挂到项目经验上。

举个例子,我在某即时通讯项目里用Redis做会话列表,遇到过缓存穿透:大量用户查询的会话Key不在缓存中,请求直接打到数据库。我的解决方法是:对不存在的会话也缓存一个空值并设置短过期时间;同时加布隆过滤器拦截明显不存在的Key;最后在代码层面加了一个本地锁防止同一个Key同时穿透。这道连锁追问下来,数据库、缓存、并发控制就全串起来了,比单独回答八股题有用得多。

4. 算法工程师专面:模型推导、AUC和深度学习追问

4.1 基础模型与手推公式

算法岗笔试之后的专业面,几乎都逃不过手推公式。最常遇到的是逻辑回归和softmax多分类。

逻辑回归的核心是“线性模型 + sigmoid”,但面试官要的不是背公式,而是推导过程。我当时按这四步准备:

  • 写出建模形式:P(y=1|x) = 1/(1+e^{-x});
  • 定义交叉熵损失函数;
  • 对参数求偏导;
  • 得到梯度形式,再解释迭代更新。

softmax要能手推交叉熵对 logits 的偏导,结果记忆很关键:第 i 个样本的预测概率减去其真实标签。这个形式在反向传播里经常用到,答好了能证明你读过代码而不是只会调框架。

SVM也从对偶问题问过,但出现频率低于逻辑回归。不需要把KKT条件背得一字不差,但要能说明:为什么引入核函数、为什么求对偶问题、支持向量是哪几个点。树模型则几乎必问集成学习:GBDT和XGBoost的区别、随机森林的随机性体现在哪里、为什么Bagging能降低方差而Boosting能降低偏差。

4.2 特征工程与样本不均衡

算法面试里项目面通常比公式面刷人更狠。特征工程这一块,高频问题包括:

  • 连续特征要不要离散化,离散化会丢失信息吗;
  • 缺失值用均值、中位数还是模型预测;
  • 类别特征怎么做编码,目标编码的风险是什么;
  • 怎么组合交叉特征。

这个问题没有绝对答案,关键是讲清楚取舍。我一般会回答:连续特征在LR这类线性模型里可以结合业务含义分桶,比如年龄段,但在树模型里直接输入通常问题不大;在深度学习模型里,可以尝试不做离散化而让模型自己学非线性关系。

评估指标是另一个必考点。分类任务要看正负样本比例:在极度不均衡场景下,准确率没有意义,重要指标是精准率、召回率、F1和AUC。面试官让我解释过“AUC的含义是什么”,正确回答是“随机抽一个正样本和一个负样本,模型给正样本的预测分大于负样本预测分的概率”。说完这个定义,再说明AUC对样本不均衡不敏感,因为它是排序性质的指标。如果面试官继续追问“阈值怎么选”,可以从业务成本角度出发,比如医疗倾向高召回,风控倾向高精准。

4.3 深度学习的经典追问

2020年算法岗面试,深度学习是常规题。我整理过一套“对症下药”的思路:

过拟合怎么办——先定位是高方差还是高偏差。增加训练数据、加正则、做Dropout、早停、数据增强,都是常见手段。但要提醒自己,不是一上来就上最好用的正则,而是先判断问题来源。

BatchNorm的作用——我说三个层面:归一化数据分布,解决内部协变量偏移;允许更大学习率,加速收敛;减弱对初始化和噪声的敏感。如果能画出它对梯度的影响更好。

激活函数选择——ReLU能缓解梯度消失,但可能出现神经元死亡;LeakyReLU加了一个负斜率;Sigmoid在非最后一层基本不建议用。面试官可能追问“为什么用Softplus或GELU”,尽量给出平滑性和计算代价的权衡。

优化器区别——SGD、Momentum、AdaGrad、RMSProp、Adam,重点说清楚每个优化器的动量思想和自适应学习率思想。Adam收敛快但有时会错过极小值,这时候可以提到AdamW或者带学习率衰减的SGD。

4.4 项目里的模型改造细节

算法面很容易出现的场景是:面试官拿着你的简历问“这个模型效果不好你会怎么迭代”。我当时准备了一个外卖配送时长预测的项目,被问了三个层次:

  • 用了什么特征和模型,为什么不用更复杂的模型;
  • 损失函数怎么设计,如果异常值很多怎么办;
  • 离线评估指标和线上指标怎么对齐。

这种开放问答拼的不是背题,而是你是否思考和复盘过自己的方案。回答时要注意承认方案局限,然后给出改进路径。比如“我当时用了分桶平均作为baseline,梯度提升树做了初版,后来发现取件时间这类特征有很强的周期模式,于是加了时序特征,最终把MAPE降低了15%”比“我用了XGBoost,效果好”可信得多。

5. 项目讲解与系统设计:从“做过什么”到“为什么这么做”

5.1 项目讲解的STAR框架

后端和算法岗位的面试,项目讲解环节决定了面试官对你的整体印象。我用的是STAR框架:背景、任务、行动、结果。但光有框架还不行,要控制讲故事的密度。

开场三十秒先说清项目背景和你的个人贡献,不要从“我们团队做了个大系统”开始。我当时总结的句式是:“这是一个给XX场景使用的XX系统,我主要负责XX模块,要解决的问题是XX,最后通过XX方案让XX指标从XX变成XX。”说完这句,面试官通常会顺着你提到的技术词往下问,所以你自己说的每个词都要经得起追问。

比如你说“用了Redis做缓存”,就要想到后续可能被问:为什么不用本地缓存?缓存淘汰策略是什么?如果Redis挂了怎么办?我的建议是,在面试前把每个项目列出至少十个可能的追问,然后自己写答案,这个过程比多刷十道题都管用。

5.2 小型系统设计:短链接请求怎么拆

系统设计题对后端岗来说基本是必考,对算法岗也偶尔会出,但更偏“推荐排序系统”这种。我遇到的经典题目是“设计一个短链接系统”,正好用来讲清楚面试官期待的回答路径。

第一步是确认需求:每日新增多少链接、QPS是多少、跳转延迟要求、需不需要统计点击。不要一上来就画存储表。第二是数据量估算:假设每天新增一千万条短链,一年约三十六亿条;跳转QPS按一百倍峰值预估。第三步做接口设计:生成短链、还原短链、点击上报。第四步设计存储:短链用62进制编码,可以用发号器生成自增ID,再转成短码;为了存储和查询方便,主表里放短码、原始URL、创建时间、过期时间。第五步优化:热点链接加一层Redis缓存,降低数据库压力;失效链接用懒删除加定期清理的混合策略。答到这里,面试官一般会认可你的架构感。

如果被问“系统的瓶颈在哪”,可以从单表数据量和数据库并发两个角度说:基于短码的查询可以分库分表,按短码哈希分片;点击量统计走MQ异步上报,不阻塞主链路。这些都是通用套路,关键是每一条都要能解释清楚“为什么这么做”。

5.3 算法方向的“推荐排序”设计题

算法岗的系统设计题更偏向于“如果给你一个推荐场景,你怎么做”。我记得被问过“设计一个信息流推荐排序服务”,核心不是把模型讲得很复杂,而是理清链路:召回环节用什么策略,协同过滤、双塔模型还是热门兜底;粗排过滤和精排序怎么衔接;排序模型用哪些特征;上线怎么做AB实验。

这类题考察的是工程化思维,面试官可能追问“双塔模型为什么比内积模型更适合召回”“线上特征延迟怎么办”。我的经验是:少讲“我们用了一个很牛的大模型”,多讲“离线训练和线上推理的一致性”“特征实时性如何保证”。能够把系统链路讲完整,比堆模型名词重要得多。

6. 复盘与避坑:offer没拿到,问题不一定出在技术

6.1 我踩过的五个坑

整理下来,有几个错误非常典型,后来自查发现每个都有对应的补救方式。

第一,准备全套知识但没准备“一句话介绍”。很多面试开场是“你先介绍一下自己”,我一开始讲了三分钟项目背景,面试官不得不打断。后来我控制到一分钟,先讲技术栈、再讲最有代表性的项目、最后讲为什么投这个岗位。

第二,算法题只写不聊。有一轮技术面,我拿到二元树的题,五分钟写完后干等面试官检查,场面一度尴尬。正确做法是写完主动说“这个思路时间复杂度O(n),空间递归栈O(h),有一个边界条件是空树,我再补一个测试用例”。沟通本身就是展示思维过程的一部分。

第三,数据库只背概念不排障。“索引为什么用B+树”“事务隔离级别有哪些”都答得出来,但面试官问“线上数据库CPU打满怎么排查”就卡了。后来我按“慢日志—执行计划—索引—锁等待”的顺序把排查流程背熟,把这当成一个固定工具包。

第四,算法岗位答模型时没有结合业务。面试官问“逻辑回归适合什么场景”,我当时的回答是“二分类问题”,太概念化。面试官真正想听的是“线性可分、需要高解释性、特征量大、对非线性要求不高的场景可以先拿LR做baseline”。把模型落到业务上下文里,才会让人觉得你会做算法。

第五,面完就忘,不复盘。前几场面试答错的题目,下次可能原题再现,我却仍然不会。后来我每场面试结束后二十分钟内,把记下的所有问题整理进表格,标出状态:会、不会、答得不完整。这帮助我在后续面试中避免重复失分。

6.2 面试记录表怎么用

我用一个简单的表格管理面试进度,列头包括:公司类型、岗位方向、技术轮次、被问的问题、当场表现、需要补的内容。这个表最大的价值不在“记”,而在于“找出知识盲区”。比如连续两家公司都问了Redis持久化,说明这是高频点,我就去看RDB和AOF的源码设计;连续答错同一类动态规划,就说明表没刷透,需要做专题训练。

面试频率高的时候,我会在周五统一复盘当周所有问题,把答得不顺畅的题目重新写一遍。这样一周下来,基本能把高频盲区清零。不要小看这个笨办法,它比漫无目的刷题有效得多。

6.3 心态管理的几点体会

春招周期长、变数多,“被挂”是常态。我第一次被某家企业挂掉的时候,一度怀疑是技术不够,后来才发现主要是岗位不匹配:对方想要更多工程落地经验的候选人,而我当时偏重算法。匹配度这件事,不应该用一次失败完全否定自己。

我给自己的调整办法是,把面试当成一次信息收集:每一轮面试都有机会了解不同团队在做什么、用什么技术栈、重视什么能力。即使没谈成,也能知道自己和市场需求之间的差距。心态放平之后,后续面试反而更松弛,沟通也更顺畅。

最后再说一个适用于所有现场面试的小技巧:回答问题前保留两三秒思考时间,用“我先说下核心结论,再展开细节”开头。这样可以避免开场就跑偏,也能让面试官更容易跟上你的思路。面试中的条理性,往往比标准答案更能给面试官留下印象。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进