先说结论:这份笔试题的含金量比很多人想象中要高出不少。点我达是即时物流赛道里的老玩家,业务本质是“订单调度 + 骑手路径规划 + 实时定价”,这意味着它的算法笔试不会只考死板的排序和递归,而是会带着明确的业务倾向去考察候选人的算法基本功。2019届校招整体处在一个算法岗需求爆发的阶段,各家都在抢人,题目难度对标的是“能干活、懂优化、会建模”的准工程师,而不是刷题机器。
这篇文章我会完整复盘这套笔试的考查逻辑、核心算法考点的拆解方式、几类典型题目的解题思路,以及我在实际备战和复盘过程中踩过的坑。如果你是准备物流、O2O、即时配送类公司算法岗的应届生,或者正在刷题阶段想找一份“有业务代入感”的笔试练手,这篇内容值得你花二十分钟认真看一遍。
1. 整体设计思路:这份算法笔试在考什么
1.1 即时物流场景对算法岗的核心诉求
点我达做的是同城即时配送,用户下单到骑手接单、取货、送达,整个过程通常控制在半小时到一小时之内。这个业务模式决定了算法团队的核心任务集中在四个方向:订单分配、路径规划、ETA预估(预计到达时间)、动态定价。这四个方向背后对应的算法能力其实是完全不同的技术栈:
- 订单分配:组合优化、贪心策略、二分图匹配、多目标优化
- 路径规划:图论、Dijkstra、A*、TSP/VRP 变体、动态规划
- ETA预估:回归模型、GBDT、特征工程、时序特征处理
- 动态定价:运筹优化、强化学习、机制设计
笔试题的命题人在出题时,不太可能直接考察这些业务模型的完整实现,因为一场笔试的时间根本不够。但他们会把业务问题抽象成经典的算法原型,然后考察你能不能识别出来、能不能用最优或近似最优的方式求解。
这就是为什么这份笔试里会同时出现贪心、动态规划、图论、字符串匹配和概率统计类的题目。每道题背后基本都对应着一个真实的业务场景抽象,只是表面上看起来是一道“标准算法题”。
1.2 题型结构与难度梯度设计
从2019届的整体情况来看,这类校招算法笔试的题型结构大致呈金字塔形分布。基础题占比大约在40%,考查的是所有计算机科班学生都应该掌握的数据结构和基础算法;进阶题占比约40%,需要你能够灵活组合多种算法思想;压轴题占比约20%,通常是一道综合性很强的场景题或者需要精细优化的难题。
基础题部分不是单纯送分,而是在筛选“基本功是否扎实”。比如链表反转、二叉树遍历这类问题,如果你还需要思考很久,说明平时的代码量不够。进阶题则直接对标业务中真实的算法挑战,比如如何在海量订单中快速给骑手分配最合适的任务,这背后其实就是带权二分图匹配问题,只不过笔试题会把它简化为一个更纯粹的数学形式。压轴题往往会让很多人直接放弃,但恰恰是这道题区分了“能拿到offer的人”和“能拿到sp的人”。
1.3 为什么掌握“算法原理”比“背诵模板”更重要
这里我想说一个很多应届生容易踩的误区。我见过不少候选人,LeetCode刷了三四百题,一上来就是“这道题我用DP做过类似的”,结果一追问状态转移方程为什么这样设计、能不能优化空间复杂度、如果数据规模扩大一百倍怎么办,就答不上来。这种人往往就是靠背模板刷题刷出来的,碰到原题能AC,但稍微做点变形就懵。
这份笔试题的命题人显然是有意在这类候选人身上做筛选的。题目本身不一定很难,但很多题会刻意变换包装,让你无法直接套用模板。比如经典的“编辑距离”问题,业务版会变成“骑手在配送途中需要经过n个地点,由于交通管制需要临时改动一个地点的到达顺序,求最少改动次数”。识别出这是编辑距离问题还不够,你还需要根据业务约束调整状态转移的边界条件。
所以我反复强调一个观点:刷题的价值不在于记住了多少模板,而在于能否快速识别一道题背后真正想考察的算法思想,并根据题目约束条件灵活调整。这个能力只能通过“理解原理 + 大量变体练习”来获得,没有捷径。
2. 核心算法考点拆解:这些题必须拿分
2.1 高频基础考点:排序、二分、栈与队列
排序算法在每年的校招笔试里都是“必考但未必直接考”的存在。所谓必考,是因为很多题目的中间步骤都依赖排序;未必直接考,是因为命题人很少会出一道“请实现快速排序”这样直白的题目。更常见的考法是让你分析某个排序算法在特定数据分布下的时间复杂度和稳定性,或者让你用排序去解决一道看似无关的问题。
我在这套题里印象比较深的一道题是这样的:有n个订单,每个订单有一个截止时间和一个配送收益,骑手一次只能配送一个订单,问如何选择订单集合使得总收益最大。这道题的第一反应可能是动态规划,但实际上正确的解法是“按截止时间排序 + 贪心选择 + 最小堆维护”。思路是:按截止时间从小到大处理每个订单,把收益加入最小堆,如果当前已选订单数超过了当前处理的截止时间,就弹出收益最小的订单。这样能保证在任意时刻选择的都是当前收益最大的合法集合。
二分法的考察频率也很高,但很少考“在有序数组中查找目标值”这种模板题。更常见的是“二分答案”类的题目,比如最小化最大配送时长、最大化最小间距这类。这类题有一个非常通用的判断标准:如果题目里出现了“最大值最小”或者“最小值最大”这样的字眼,大概率就是二分答案。关键是要写出正确的check函数,这部分很考验代码功底和边界处理能力。
栈和队列的考察则经常隐藏在表达式求值、括号匹配、滑动窗口最大值这类题里。这类题本身不难,但如果对数据结构不够熟练,很容易在细节上翻车,比如栈空判断、窗口左右边界的移动时机等。我的建议是这些基础数据结构一定要练到手写无误的程度,因为它们是后面所有复杂算法的基础设施。
2.2 字符串算法:KMP的next数组到底怎么理解
字符串算法在物流类公司的笔试题里出现频率很高,原因很简单:配送地址的匹配、关键词搜索、订单备注的语义解析都离不开字符串处理。而在所有字符串算法里,KMP是最常被考察的,因为它既包含了对暴力匹配的优化思想,又有足够多的细节可以考查候选人是否真懂。
KMP的核心思想是:当模式串与主串在某一位不匹配时,不需要像暴力算法那样把模式串整体右移一位重新开始比较,而是利用已经匹配的前缀信息,把模式串直接滑动到合适的位置。这个“合适的位置”就是通过next数组来记录的。
以模式串“abacaba”为例,我来说一下next数组的计算过程。next[i]的定义是模式串前i个字符组成的子串中,最长相等前后缀的长度。对于“a”,没有真前后缀,next为0;对于“ab”,前缀和后缀没有相等的情况,next为0;对于“aba”,前缀“a”和后缀“a”相等,最长长度为1,所以next为1;对于“abac”,最长相等前后缀长度为0;对于“abaca”,前缀“a”和后缀“a”相等,长度为1;对于“abacab”,前缀“ab”和后缀“ab”相等,长度为2;对于“abacaba”,前缀“aba”和后缀“aba”相等,长度为3。
很多教材直接给代码让你背,但我推荐另一种理解方式:next数组本质上是在对模式串自己做一次KMP匹配。当计算next[i]时,我们实际上是在模式串的前i-1个字符中寻找最长的“既是前缀又是后缀”的子串。理解了这个,你才能真正体会到KMP为什么能把时间复杂度从O(m*n)降低到O(m+n)。
笔试中关于KMP的考法通常有三种:直接考察next数组的计算、考察KMP在字符串匹配中的应用、以及考察KMP的变体(如求字符串的最长回文前缀)。如果你能熟练手写next数组的构造过程,并且能解释清楚“为什么失配时要回退到next[j-1]”,这道题基本就稳了。
2.3 动态规划:状态定义是灵魂
动态规划在算法笔试中的地位不用多说,基本是每套题的必考项。但很多人在DP题上的表现是“看答案能看懂,自己做就想不出来”,根本原因在于状态定义这一步没有形成方法论。
我个人的经验是,做DP题的时候先不要急着写代码,而是强迫自己回答三个问题:第一,这个问题的最优解如何由子问题的最优解组合而成;第二,用什么维度来刻画子问题的状态;第三,状态之间如何转移。
以经典的“编辑距离”为例,状态定义是dp[i][j]表示把字符串A的前i个字符转换成字符串B的前j个字符所需要的最少操作数。转移方程就是考虑最后一步操作是插入、删除还是替换。这个定义想清楚了,代码就是水到渠成的事。但如果状态定义错了,后面所有的工作都是白费。
物流场景里,DP最常见的应用是路径规划相关的变体问题,比如“骑士在棋盘上的最短步数”“在网格中从左上角到右下角的最小代价路径”等。这些题的共同特点是:子问题之间具有重叠性,且存在明确的状态转移关系。建议在备战阶段把背包问题、最长上升子序列、编辑距离、区间DP这四类经典问题彻底吃透,因为大多数校招DP题都是它们的变体。
这里再补充一个技巧:如果一道题直观上可以用递归求解,但递归会重复计算大量子问题,那么这道题大概率可以用记忆化搜索或者DP来优化。记忆化搜索是自顶向下,DP是自底向上,两者本质上是对同一张状态表的两种遍历方式。对于某些状态转移关系不够直观的题,先用记忆化搜索写出正确版本,再改成DP,是一个很实用的策略。
2.4 图论与贪心:业务场景的隐形主角
图论算法在即时物流领域的地位怎么强调都不过分。每一个骑手都在城市路网上移动,每一笔订单都对应着起点和终点,整个调度系统的核心就是如何在图上做优化。但校招笔试通常不会直接考完整的最短路径或最小生成树,而是会用一个实际问题来包装。
比如“配送员从配送站出发,需要给n个客户送货,每个客户有一个时间窗口,问能否在截止时间前全部送达”这类题目,本质上是一个带约束的最短路径问题。在数据规模较小的情况下,可以用状态压缩DP来求解;数据规模较大时,往往需要贪心策略配合优先级队列。
贪心算法的难点在于证明贪心策略的正确性。笔试的时候不需要写严格的数学证明,但你至少要能用自己的话说清楚“为什么这个局部最优选择不会影响全局最优解”。很多时候,考官在面试环节追问的其实就是这个点。比如经典的活动选择问题,按结束时间最早排序是最优的,原因在于越早结束的活动给后续留下的时间越多,这个直觉论证在笔试阶段就足够了。
我自己在复盘这套题的时候发现,命题人非常喜欢把贪心和堆组合起来考。前面提到的“截止时间 + 收益”的订单选择问题就是一个例子。这是很有业务代表性的:现实中的骑手配送订单,每个订单都有不同的配送难度和收益,系统需要实时决策优先接哪个单。堆在这里的作用是维护“当前已选订单中收益最小的那个”,以便在需要替换时快速找到它。这种组合型的考法,就是命题人有意识地在考察候选人能否把多种算法工具串联起来解决复杂问题。
2.5 快速幂、位运算与数学思维题
除了上述几大类,校招算法笔试里通常还会有一两道考察数学思维的题,用来测一测候选人的智商上限。这类题看起来跟业务没什么关系,但实际上是在考察“遇到陌生问题时,能否快速找到规律并建立数学模型”的能力。
快速幂就是一个典型的例子。它的应用场景很广泛,比如计算某个数的大次幂取模,在密码学、随机数生成、概率计算里都会遇到。快速幂的核心思想是把指数进行二进制分解,从而把时间复杂度从O(n)降到O(log n)。我在很多套笔试题里都见过这道题的变体,比如计算斐波那契数列的第n项(用矩阵快速幂)、计算某个递推式的第n项等。
位运算题也经常出现,因为很多最优解都隐含在二进制的特征里。比如“在一个数组里,只有一个数字出现一次,其他数字都出现两次,找出这个数字”,最优雅的解法就是用异或运算。这种题考察的不是知识储备,而是思维能力,所以临时抱佛脚很难奏效,更多靠平时的积累和反应速度。
我做这类题的一个经验是:如果一道题看起来涉及“大数”“多次幂”“奇偶性”“倍数关系”,优先考虑快速幂、取模运算、位运算这三个工具。它们不一定是最优解,但往往是通向最优解的突破口。
3. 实战视角:从真题场景到解题流程
3.1 典型题目类型与解题路线对照
在校招算法笔试的备考过程中,比刷题更重要的是建立“题目类型 → 算法范式 → 代码模板”的映射体系。我在这套题里总结出了六类最高频的题目原型,每类都有一个对应的核心优化思路。
第一类是“最大最小”类,典型特征是题干里出现了“最大值最小”“最小值最大”这类表述,核心算法是二分答案。第二类是“最优选择”类,典型特征是“按照某种规则挑选元素使得某个目标最大/最小”,核心算法是贪心加排序以及堆的辅助。第三类是“路径规划”类,典型特征是图上的移动与代价计算,核心算法是最短路径或DP。第四类是“模式匹配”类,典型特征是字符串子串与模式串的关系,核心算法是KMP或更进阶的AC自动机。第五类是“资源分配”类,典型特征是容量限制与价值最大化,核心算法是背包DP。第六类是“组合计数”类,典型特征是方案数量统计,核心算法是DP加排列组合。
这个映射关系整理好之后,你会发现刷题效率会有质的提升。原因是人的大脑记忆单个题目的能力是有限的,但记忆“问题特征”和“算法范式”的匹配关系却可以做到很高的准确率。看到一道新题的时候,先让它归入某个已知类别,再套用对应的方法做适配,这比从零开始想解法要快得多。
3.2 手写代码的规范性与性能意识
笔试通常要求手写代码,不管是在线IDE还是白板,代码的规范性都会被考官暗中考察。我在这套题里获得的亲身体验是:笔试中即使提交代码完全正确,一段结构混乱、变量命名随意的代码也很容易被压低评价。因为面试官在筛简历的时候,会把你笔试时的代码当作代码风格的第一份样本来评判。
我的具体建议是:变量名要能表达含义,不要用a、b、c这种没有信息的名字;函数要尽量拆分,不要把几十行逻辑全塞在一个main函数里;关键的边界条件用注释标注出来;时间复杂度如果比较微妙,在代码末尾简单说明一下你的思路。
在手写代码时的性能意识也很重要。这里说的性能不只是算法的时间复杂度,还包括常系数。两个时间复杂度和空间复杂度完全相同的解法,实际运行耗时可能相差数倍。比如用栈模拟递归实现DFS、用数组而不是链表、避免在循环体内创建大对象、提前break减少无效迭代、用快读快写处理大数据输入,这些细节在笔试中可能不会成为瓶颈,但在面试代码评审时,面试官会注意到这些工程意识。
3.3 从笔试到面试的衔接策略
一个容易被忽视的点是:笔试不是终点,面试官会在面试环节追问笔试题目。我在校招季经历过几次这样的流程,面试官会直接打开我之前提交的代码,问我“为什么这里用贪心而不是动态规划”或者“如果数据规模增加一个数量级,你的方案会怎么演进”。
这种追问其实是一个展示自己的好机会。如果你的笔试代码只是套了一个模板而没有深入理解,那么两三句追问就会露馅。反过来,如果你在笔试之后认真复盘了自己的每道题,整理了每道题的多种解法和复杂度分析,面试官问到任何一个细节你都能流畅回答,这会给面试官留下非常深刻的印象。
我建议在校招季准备一份自己的“笔试复盘文档”,每道题记录三个信息:自己的原始解法、更优解法、两种解法的差异分析。这份文档在整个面试季的价值甚至会超过刷题数量本身,因为它展示了你的总结和反思能力,这是面试官非常看重的素质。
4. 校招算法笔试的备战路线与常见问题排查
4.1 三个月备战计划:从基础到冲刺的执行路线
校招笔试的准备切忌盲目刷题。我见过太多人从三月份开始每天刷五道题,刷到九月份发现能力提升非常有限,原因就是没有体系化的规划。我自己在实践中摸索出的一个三个月计划,适用于算法基础中等水平的同学,大家可以参考。
第一个月是基础夯实期。目标是过一遍所有常考的数据结构和基础算法,不需要大量刷题,但要求能把核心数据结构的手写实现写出来,比如链表、栈、队列、二叉树、堆、并查集、图的基本存储方式。这个阶段的参考书不需要多,经典的数据结构教材加上LeetCode的题目分类功能足够。每学完一个数据结构,就做十道左右的对应练习题,目的是熟悉这个数据结构典型的使用场景。
第二个月是专项突破期。目标是熟练掌握四大算法思想:枚举与模拟、贪心、分治、动态规划、搜索(BFS/DFS),以及图论里的最短路径和最小生成树。这个阶段建议按专题进行刷题,每天专注一个算法专题做3至5道题,难度从简单到中等再到困难递进。做完之后一定要看题解,重点对比自己的解法与最优解法的差距。这一步看似浪费时间,其实是提升速度最快的环节。
第三个月是模拟冲刺期。目标是适应笔试的节奏和氛围。建议每两天做一套完整的真题或高质量的模拟题,严格按照考试时间(通常90到120分钟)来计时。做题的时候模拟真实环境:不看手机、不查资料、一次性提交。做完之后花两倍的时间进行复盘,把每道题在脑海里重新过一遍,分析时间分配是否合理、哪些题不值得浪费太多时间、哪些题本来可以做对但因为粗心做错了。
4.2 常见失分点与避坑技巧汇总
在校招笔试里,有很多“非智力因素”导致的失分,非常可惜。我把这些年看到的和亲身体验过的失分点整理成了下面这张速查表,希望对大家有帮助。
| 失分类型 | 典型场景 | 避坑建议 |
|---|---|---|
| 边界条件遗漏 | 数组为空、只有一个元素、首尾元素 | 写完代码后手动跑一遍边界用例 |
| 输入读取错误 | 多组输入、行末空格、超大整数 | 先用小数据验证输入解析 |
| 循环变量越界 | while循环中没有及时break | 注意循环退出条件,尤其是涉及索引加减时 |
| 数据规模预估错误 | 低估时间复杂度导致超时 | 先算数据量,再选合适算法 |
| 状态转移遗漏 | DP数组初始化错误导致结果偏移 | 画状态表,手工推演前几步 |
| 贪心证明缺失 | 只写解法但说不出为什么对 | 面试前准备几个经典贪心的直观论证 |
这里我特别想强调边界条件这一项。很多人在笔试时程序出错,不是思路不对,而是没有考虑数组越界或者空数组的情况。我的习惯是每写完一道题,立刻在草稿纸上写下三组测试用例:最小规模、普通规模、最大规模,然后把自己代入代码逻辑逐行走一遍。这个习惯刚开始很费时间,但练多了之后速度会越来越快,而且能显著提高一次提交通过的概率。
4.3 实战复盘:我做这套题时的答题节奏与心态管理
讲一个我个人的真实体验。做这套题的时候,我的答题策略是“先易后难、定时放弃”。拿到试卷后,先用两分钟把所有题目快速浏览一遍,把题目按“送分题”“常规题”“硬骨头”分成三类。然后从送分题开始做,确保基础分全部拿到;再处理常规题,每道题给自己设定一个时间上限;最后剩下的时间集中攻硬骨头。
具体的时间分配大概是:送分题30%的时间,常规题50%的时间,压轴题20%的时间。如果一道压轴题想了十五分钟还是没有明确的思路,我会果断把已经在纸上写出的部分思路写到答案里,然后跳过去做下一题。经验证明,与其在一道难题上死磕,不如多拿几道中等题的满分。
心态管理方面有一个很重要的原则:每道题做完之后不要急于自查对错,先把整张卷子能拿的分都拿到,然后再用剩余时间回头检查。笔试过程中千万不要因为一道题做不出来就产生强烈的挫败感,因为校招笔试的时间本来就紧张,大部分人都无法做到满分。你要做的只是比同岗位竞争者多拿几分,重点是把确定能做对的题做对,不留无谓的失分。
4.4 校招算法岗的长期学习建议
写到这里,我想把视角拉远一点。校招笔试只是算法工程师职业道路上一个很小的节点,即使你拿到了理想的offer,真正进入工作后也会发现,学校里学的算法和工业界实际用到的算法之间还有很大的鸿沟。学校里重点考察的是“给定明确约束条件后你能不能在规定时间内写出最优解”,而工业界更看重“在真实、不完美的数据中你能否设计出鲁棒、可扩展、可维护的算法方案”。
因此在备战笔试之余,我建议对算法方向有长期规划的同学尽早接触一些工业界的工具和思想。比如学习如何使用Spark或Flink做分布式数据处理,了解线上机器学习模型是怎么做特征实时计算的,尝试用Python实现一个简单的策略迭代或者价值迭代去解决一个真实的调度问题。这些经历不会直接体现在笔试分数上,但会在面试中成为你的独特加分项。
另外一点是保持对算法前沿的好奇心。比如近几年很火的强化学习在调度系统中的应用、图神经网络在路网建模中的应用、大模型在物流文本理解中的应用,都是非常有潜力的方向。校招只是起点,真正拉开差距的是入职后持续学习的动力和方向感。
最后分享一个小技巧:试着在刷题之外给自己设定一个“算法项目”。随便选一个真实的业务问题,比如“设计一个简单的外卖订单分配模拟器”,然后用你学过的算法去实现它。这个项目不需要很大的规模,但从中你能体会到从“做题”到“做产品”的思维方式转变,这种体会是刷几百道题都换不来的。希望这篇复盘对你有所帮助,也祝你能在笔试题里发挥出自己真正的水平。