ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

Momenta秋招笔试复盘:算法、深度学习与C++备考指南

2026/8/31 14:31:02 拓冰建站 浏览量
Momenta秋招笔试复盘:算法、深度学习与C++备考指南 2019年那阵子自动驾驶赛道正是最热的时候Momenta作为国内头部玩家技术岗笔试的含金量和筛选强度都不低。我当时投的是算法岗拿到题的瞬间第一反应是“这真的是笔试吗”而不是“这题我会不会”——因为它的考察维度很杂除了常规的数据结构和算法手撕还有不少数学推导、深度学习原理和C底层的硬核问题。如果你是准备自动驾驶方向校招的同学这套题型的思路至今都有参考价值。这篇文章把我的做题经历、考点复盘、典型题解法以及踩过的坑整理成一份可以“抄作业”的备考笔记。无论你投的是感知、规划、控制还是基础架构方向这套笔试题背后隐藏的能力模型是共通的基础扎实、代码干净、能快速把算法问题抽象成可实现的工程方案。1. 内容整体设计与思路拆解1.1 Momenta技术岗笔试的底层逻辑先说结论Momenta 2019秋招技术岗笔试不是单纯考刷题量它更像一张“能力雷达图”覆盖四个维度——数学基础、算法与数据结构、深度学习原理、C/Python工程能力。为什么这么设计因为自动驾驶公司要的人不是只会训练模型的人而是能落地的人。感知团队需要你懂图像处理的底层逻辑甚至要会手推反向传播规划团队需要你快速写出稳定的路径搜索算法基础架构团队需要你写出高性能、无内存泄漏的C代码。一套笔试题同时筛这四类能力靠的就是不同模块的组合。我记得当时题型分布大致是这样模块题型主要考察点数学与概率选择题计算题矩阵、概率分布、期望方差算法与数据结构编程题链表、二叉树、动态规划深度学习简答手推卷积原理、反向传播、优化器工程基础代码阅读填空题C内存、多线程、Python特性1.2 它解决的核心问题筛选“能做事的人”笔试只是第一关但它是性价比最高的一关。面试可以看你思考方式但笔试能在两小时内快速判断你是否具备基本盘。我记得当时考完和一个朋友聊他算法题全AC了但C那几道指针题几乎全空最后挂在了流程里。不是说算法不重要而是对自动驾驶技术岗来说工程能力本身就是技术能力的一半。这套笔试的另一个隐性逻辑是“压力测试”。两个小时的限时题目量大概在10-15道之间包含选择题、填空题、简答题和编程题时间非常紧张。设计者的意图很明确在没有足够时间反复纠结的情况下你暴露出来的就是你平时最真实的技术习惯。写完这道题你平时有没有认真抠过TensorFlow的卷积实现有没有自己动手写过智能指针有没有关注过内存对齐——全部一览无余。1.3 为什么这套题值得反复研究我不是让你去找2019年的原题背答案而是让你理解这套题背后的出题思路。这几年我看过不少自动驾驶公司、大厂和AI独角兽的笔试题万变不离其宗。你搞懂了Momenta这套笔试为什么这么出再看其他家你会发现很多题目都是同一个套路。比如深度学习考察很多公司只会问“BatchNorm的作用是什么”这种概念题但Momenta更喜欢让你推导一个卷积层的参数量、FLOPs然后反推感受野。这种题目没有标准答案却能区分“背过八股文”和“真懂原理”的人。这种出题方式后来成了很多自动驾驶公司笔试的模板。2. 核心细节解析与实操要点2.1 数学与概率不只是公式要会算数学部分我印象深刻的有几类。第一类是矩阵相关的计算比如给你一个变换矩阵问它对一个点做旋转和平移之后的坐标是多少。这类题在SLAM、传感器标定里非常常见所以基本是必考。第二类是概率和统计比如“抛一枚不均匀硬币正面概率p求连续抛出n次至少出现k次正面的期望”之类的变形题。这类题的核心不是记公式而是理解随机变量的分解技巧。我后来总结了一套自己的方法遇到期望类题目先试着把复杂随机变量拆成若干简单变量之和用线性期望去算。这种做法几乎能解决90%的期望题。比如二项分布的期望是np很多同学直接背下一次换成超几何分布就懵了。你可以把每个位置看作一个01变量期望是p整个序列的期望就是np这个思路其实比背公式更快更稳。实操建议准备期别只看凸优化和概率论的目录一定要动手算算到看到一个分布能条件反射写出它的期望、方差、矩母函数为止。笔试现场不允许你慢慢回忆公式。2.2 算法与数据结构高频题型的“肌肉记忆”算法部分是硬骨头。Momenta 2019那场编程题大概是三道左右考查重点集中在链表、二叉树、动态规划、排序和二分查找这几个大类。我记得有一道链表题是要在O(nlogn)时间内完成排序很多同学第一反应就是快排但链表不支持随机访问所以正解是归并排序的链表版本。这种“经典题变种”是笔试的常态。它们考验你在限制条件下灵活调整算法的能力。我建议你别只刷LeetCode的高频题多练习这些变种链表反转链表、链表排序归并、删除倒数第N个节点、判断环二叉树层序遍历变种、最近公共祖先、二叉树转双向链表动态规划背包变形、最长上升子序列、编辑距离、股票系列二分查找变种很多比如找旋转数组最小值、找峰值、二维矩阵搜索具体到代码实现要养成一套固定习惯。比如链表题先考虑要不要引入dummy节点二叉树题先想是递归还是迭代空间复杂度有没有限制。这种习惯在笔试现场能帮你减少大量debug时间。2.3 深度学习原理性与工程性并重深度学习部分是我觉得最“自动驾驶特色”的模块。不单纯问你“什么是注意力机制”而是要把结构和数学原理揉在一起考。我记得有题问的是卷积操作在特征图上的感受野计算另一题是关于BatchNorm的训练与推理阶段区别还有一题是让推导一个两层网络的反向传播过程。我当时最大的感受是如果只是用过PyTorch、TensorFlow写模型而不理解底层数学这部分的题基本拿不到分。比如感受野计算不是说你会调卷积核大小和步长就行了你得理解每一层输出特征图上一个像素对应原图的区域大小。这个在目标检测、分割模型的设计里是基本功。给个简单的公式对于连续卷积池化层输出特征图每个元素对应的感受野大小和所有层的卷积核大小、步长、池化大小都有关系。笔试里常见的考法是给你一个网络结构表让你算最后一层的感受野。我建议你手动推一遍LeNet或者VGG的感受野增长过程推完整个笔试的“卷积计算题”就稳了。2.4 C工程基础藏在细节里的陷阱C部分对没有扎实底层功底的算法同学来说杀伤力最大。我印象很深的是几道关于智能指针和多线程的题。比如“shared_ptr是否线程安全”、“weak_ptr如何解决循环引用”、“移动构造和拷贝构造的区别”以及“vector的扩容机制底层是怎么实现的”。这些题看上去都是八股文但真正写过大规模C项目的人会发现这些细节会直接影响程序稳定性。自动驾驶车辆上的感知、规划模块通常是7x24小时运行任何内存泄露、野指针、数据竞争都可能造成严重后果。所以面试官问这些其实是在判断你有没有“硬工程能力”。我给你的建议很直接笔试前把《Effective Modern C》里的关键章节过一遍尤其是智能指针、移动语义、lambda表达式、多线程同步这几块。并且一定要自己动手写几个小例子去跑只看书是记不住的。3. 实操过程与核心环节实现3.1 典型编程题与完整解题思路下面我完整复盘几道我当时遇到的或类似的题目附上思路和代码这个过程比题目本身更有价值。题目1实现一个LRU Cache要求get和put操作的时间复杂度都是O(1)。这是缓存淘汰策略里的经典题目也是相机制作和调度系统里很常用到的结构。底层结构需要哈希表双向链表哈希表保证O(1)查找双向链表保证O(1)插入和删除。#include unordered_map struct Node { int key, value; Node* prev; Node* next; Node(int k, int v) : key(k), value(v), prev(nullptr), next(nullptr) {} }; class LRUCache { private: int capacity; Node* dummyHead; Node* dummyTail; std::unordered_mapint, Node* dict; void removeNode(Node* node) { node-prev-next node-next; node-next-prev node-prev; } void addToHead(Node* node) { node-next dummyHead-next; node-prev dummyHead; dummyHead-next-prev node; dummyHead-next node; } public: LRUCache(int cap) : capacity(cap) { dummyHead new Node(0, 0); dummyTail new Node(0, 0); dummyHead-next dummyTail; dummyTail-prev dummyHead; } int get(int key) { if (dict.find(key) dict.end()) return -1; Node* node dict[key]; removeNode(node); addToHead(node); return node-value; } void put(int key, int value) { if (dict.find(key) ! dict.end()) { Node* node dict[key]; node-value value; removeNode(node); addToHead(node); } else { Node* node new Node(key, value); dict[key] node; addToHead(node); if (dict.size() capacity) { Node* last dummyTail-prev; removeNode(last); dict.erase(last-key); delete last; } } } };这道题在笔试现场容易犯两个错误一是忘记处理capacity为0的极端情况二是删除节点后没有同步清理哈希表。前者会导致空指针访问后者会导致内存泄漏。在写代码之前先和面试官或阅卷系统确认边界条件这是一种很好的工程习惯。题目2最大连续子数组和Kadane算法变种LeetCode 53的变种几乎每场技术笔试都会出现。基础版本用动态规划状态转移方程是dp[i] max(nums[i], dp[i-1] nums[i])结果取dp数组的最大值。空间可以压缩到O(1)。int maxSubArray(std::vectorint nums) { int currentSum nums[0]; int maxSum nums[0]; for (int i 1; i nums.size(); i) { currentSum std::max(nums[i], currentSum nums[i]); maxSum std::max(maxSum, currentSum); } return maxSum; }笔试中这题常见变种有两个。一个是要求输出子数组本身而不只是最大和这时候需要维护起止指针。另一个是要求数组可以循环即子数组可以首尾相接。后者是LeetCode 918做法是先算不循环的最大子数组和再算循环情况下的最大和等价于总和减去最小区间子数组和两者取较大值。建议自己推一遍循环版本的边界条件笔试现场很可能遇到。3.2 深度学习手推题的复现过程反向传播手推是笔试里比较让人紧张的一部分因为你不仅要写出结果还要展示清晰的计算过程。我建议用一种固定的流程来写画计算图标出每个节点的输入输出。设中间变量分别命名为z1、a1、z2、a2避免长表达式。从输出层往前逐层求梯度每写一步都标注“这对应链式法则的哪一项”。以一个两层的全连接网络为例输入维度是2隐藏层维度是2输出维度是1激活函数是sigmoid损失函数是均方误差。推导时重点关注链式法则中不同路径的梯度之和。很多同学会在中间层梯度那里漏掉“来自后一层两个路径的梯度相加”这是最典型的丢分点。我的心得是把反向传播当作“递归复合函数求导”而不是“矩阵运算公式”。前者不容易出错后者一旦矩阵维度对不上就全盘崩。笔试前至少手推三遍单隐层全连接、CNN的单个卷积核、Softmax交叉熵。这三类覆盖了90%的考题场景。3.3 C代码阅读与内存布局分析有一类题很迷惑人就是给你一段有内存问题的C代码让你找出问题并说明后果。典型的问题包括返回局部变量的引用、数组越界、new后没有delete、shared_ptr循环引用。这里我给一个我在做题时常用的检查顺序检查返回值类型尤其是返回引用或指针的情况检查所有new/delete、make_shared的配对情况检查for循环边界尤其是有等号还是没有等号检查类是否有虚函数如果有类成员的布局和大小计算检查多线程下共享变量的访问是否需要加锁遇到“vector扩容后迭代器失效”的题我的回答套路是这样的vector在push_back导致扩容时会重新分配一段更大的内存然后把旧的元素拷贝或移动过去此时指向旧内存的迭代器全部失效。失效的原因是旧内存已经被释放继续使用就是悬垂指针。想扩展一下的话可以提reserve提前分配也可以比较deque和list在这方面的不同这种补充会显得你理解更系统。4. 常见问题与排查技巧实录4.1 笔试时间不够用怎么办我自己经历过两次“时间不够用”的考场状态。第一次是太追求完美一道算法题写完了还反复优化导致后面的深度学习简答题只能草草写几句分数自然不好看。第二次学乖了先花三分钟把所有题目浏览一遍把每道题的预估时间写在草稿纸上超时就先跳绝不纠缠。给一个通用的时间分配策略选择题和填空题每道不超过3分钟简答题和手推题每道8-10分钟编程题每道15-20分钟。程序题如果20分钟还没有完整AC就先把暴力解法写上保证能过一部分测试点然后注明优化方向。这种策略不是投机取巧而是一种工程资源管理能力。阅卷人更愿意看到你有策略地拿分而不是在某一道题上钻牛角尖导致大面积空白。4.2 熟悉的题目突然AC不了笔试现场最容易崩的一种情况是明明刷过类似的题但换了个条件就写不出来了。比如求“最长回文子串”的题变成“最长回文子序列”解法就从中心扩展变成了动态规划。这种问题不是你不会而是刷题的时候只记了“最优解”而没理解“这个解法为什么对”。我在备考后期采用了一个方法每做完一道题强迫自己写一行“核心难点”和一行“解法动机”。比如做“旋转数组找最小值”核心难点是“有序数组被旋转后依然是部分有序”解法动机是“利用二分查找和断点的位置关系”。这个习惯让我在笔试时遇到变形题也能快速定位到对应的“解法动机”而不是机械背诵模板。4.3 代码跑通但内存出错不少同学笔试时用的是在线OJ环境写C时不小心犯内存错误却调试不出来。我遇到过最典型的情况是链表的删除操作里删除最后一个节点后没有把新的尾部节点的next置空结果遍历输出时死循环。面对这种情况我的建议是编码时脑子里始终有一份“内存关系图”每操作一次指针就确认一下它指向哪里。尤其是写链表、二叉树这类指针密集型的题目多花10秒检查next和prev的赋值顺序能省下后面30分钟的debug时间。另一个实用技巧是在所有循环体结束处检查“当前节点的前驱和后继是否都指向预期位置”。这好比开车看后视镜虽然多一个动作但能防止很多大问题。4.4 深度学习概念题拿不准怎么办遇到拿不准的概念题一个小技巧是把你知道的所有相关内容分层写出来。比如题目问“Batch Normalization的作用”你不要只写一句“加速收敛”而是展开成三层训练时它对小批量数据做标准化减少内部协变量偏移推理时使用全局统计量而不是当前批次的统计量它对初始化缩放不敏感从而可以用更大的学习率。阅卷人最怕看到一句话答案如果你能展示“我知道原理、知道它在训练和推理阶段的区别、知道它的工程影响”即使某个细节记错了整体印象也会好很多。碰到完全没见过的题比如“你知道1x1卷积的作用吗”也可以从降维、增加非线性、实现跨通道信息融合三个角度去答。哪怕是猜测也要展示出“我在主动构建知识体系”的能力而不是被动的“背题机器”。5. 备考方向与复习资源整理5.1 核心复习清单我在那次考试后把自己踩过的坑和被问到的知识点做了一个复习清单这里直接分享给你。如果你正在准备类似公司的技术岗笔试可以按这个清单横向排查数据结构数组、链表、栈、队列、哈希表、二叉树、堆、图的基本操作和复杂度算法思想二分、双指针、滑动窗口、回溯、动态规划、贪心、并查集数学基础线性方程组与矩阵运算、特征值与特征向量、概率分布、参数估计、常见不等式深度学习反向传播手推、卷积与池化的原理、常见激活函数与损失函数、Dropout与BatchNorm工程语言C内存模型、左值右值与移动语义、智能指针、进程线程与并发Python多进程、装饰器、生成器别想着一次全看完这个清单的价值在于查漏补缺。你可以在每周复习结束时对着这个清单问自己“这周我更新了哪一行的认知”逐项打勾比漫无目的地刷题效率高得多。5.2 用什么方式练脑最有效刷题不是越多越好。我见过刷了800道LeetCode但笔试依然不理想的同学问题出在“练习模式太单一”——只在IDE里写题只在题解区看答案很少模拟真实考试的紧张感。我的经验是考前一周安排至少三次完整的限时模拟题目就用各家公司往年的笔试真题电脑开成飞行模式手机放另一个房间强迫自己在两个小时内像正式考试一样答题。模拟结束后不要只看分数要逐题分析哪道题卡了超过20分钟、哪道题的边界条件漏了、哪道题的C知识点是真的不会。把这些问题记到笔记本上考前最后一天只看这个笔记本。这里有一个容易忽略的细节限时模拟时要习惯使用考场提供的本地编译器而不是你常用的IDE。因为在线OJ的编译警告处理和本地IDE不同早暴露这些差异考场上就不会被奇怪的重定向问题卡住。5.3 从笔试到面试的能力迁移虽然这篇文章重点是笔试但我想强调一点笔试题里暴露的短板面试时一定会被继续追问。比如你笔试里C内存题没答好面试官大概率会在技术面里专门准备一道更深入的智能指针题目来判断你是不是“补课”了。因此备考时不要抱“蒙混过关”的心态真要搞懂每个知识点。我当时因为笔试里一道深度学习手推题写得不够完整面试时被一直追问到“如果中间层的激活函数换成ReLU反向传播的梯度形式怎么变”。还好我备考时真的手推过ReLU的导数但如果不准备这一连串追问肯定会露馅。所以我建议你用“笔试倒逼复习面试倒逼深化”的思路把笔试当作面试的预演而不是一个孤立的关卡。6. 写在最后的体会回看2019年那次笔试我最大的收获不是“拿到Offer”而是知道自己哪块能力是虚的。C工程部分当时有多道题我靠直觉蒙对了但心里清楚如果不补上就算入职也只能做一个调包侠。后来我把智能指针、移动语义和内存序相关的书认真读了一遍练了几十个工程场景的小例子才真正觉得“自己的手是干净的”。如果你正在准备类似企业的技术岗笔试我想说的是这套题确实不简单但它考的东西都是将来工作中每天都要用的。与其说是在筛人不如说是在给你画一份“工程师能力地图”。你花两周时间把地图上的空白区域涂满收获的不仅是一场笔试的通过更是从学生思维到工程思维的一次升级。祝准备笔试的同学顺利有具体问题也欢迎在评论区交流我看到会尽量回复。