
提到“在线编程题”我猜不少经历过校招的研发工程师都不陌生。2016年那会儿美丽联合后来大家更熟悉的名字是蘑菇街、美丽说校招笔试里在线编程题就是一道绕不过去的坎。很多同学平时LeetCode刷得飞起一到笔试平台就翻车要么卡在输入输出上要么栽在复杂度上还有人是被多组测试用例活活折磨到超时。我后来自己也参与过几轮技术招聘的笔试出题和阅卷再回头看当年的笔试很多门道才真正想明白。这篇东西我就把在线编程题这件事从头到尾聊透它到底想考什么、拿到题怎么分析、有哪些坑每年都有人踩以及备考阶段怎么准备才有效率。它不是那种“背几道题就能过”的应试技巧总结而是把出题人、阅卷人和做题人三个视角放在一起讲。不管你是正在准备校招的应届生还是想跳槽的社招同学哪怕只是好奇大厂笔试怎么筛人这篇内容应该都能给你一些参考。1. 在线编程题到底在考什么一道题背后的三层逻辑1.1 从“会写代码”到“会解决问题”的跨越很多人觉得在线编程题就是考算法其实不完全是。面试官想在四十分钟到一个小时里确认的事情至少包含三个层次。第一层是编码基本功。你能不能把脑子里的思路快速转成一段语法正确、逻辑完整的代码。这一层看起来简单实际是筛掉人最多的环节。本地IDE写代码习惯了自动补全和编译报错提示到了在线编辑器里裸写很多人连include该写什么、Scanner和System.in哪个快都要犹豫半天。第二层是算法与数据结构能力。给你一个没见过的题目你能不能识别出它背后的经典模型。是排序是二分是动态规划还是图论里的最短路这一层考验的是知识迁移能力见过足够多的题型才能在看到题目时快速定位到对应的解题框架。第三层是工程意识。代码是否清晰、变量命名是否有意义、边界条件有没有考虑周全、有没有处理异常输入。笔试虽然主要靠自动判题机跑结果但很多公司会人工抽查高分段代码代码风格如果太糟糕到了面试环节会被面试官拿来讲事。这三层对应的分别是“能写”“会想”和“做得好”。在线编程题不是单纯考你会不会背某几个算法而是通过一个限时、限环境、限性能的场景把这三个层次的真实水平一次性测出来。1.2 电商公司为什么偏爱这类题像美丽联合这种电商背景的公司笔试题目往往有个特点题干会带点业务包装。比如“满减凑单”“优惠券组合”“库存分配”“购物车结算”看起来像是业务需求文档实际剥开外壳内核全是经典算法。为什么这么设计因为研发工程师入职后要面对的真实业务本质上就是把业务问题抽象成技术问题。一个订单系统要算最优优惠组合就是在做动态规划或贪心一个推荐系统要筛选候选集就是在做TopK或排序一个库存系统要分配发货仓就是在做二分图匹配或贪心策略。笔试出题人很自然地把这些业务场景封装成了算法题。所以说在笔试里看到那种题干特别长、还带着电商业务背景色的题目不要慌。先把业务词汇圈出来比如“满减门槛”“每位顾客最多买一件”“总价超过阈值”然后问自己一个问题如果不看这些业务描述这题的数学模型是什么把业务包装剥掉剩下的就是数据结构与算法的老朋友们。1.3 阅卷视角面试官到底在看什么我参与阅卷时有个很深的感受在线编程题不是“AC了就万事大吉”。自动判题机会告诉你这道题通过还是没通过但出题人会看整套试卷的分数分布。一道题如果有超过一半的人没做出来说明题出难了如果一小部分人秒过说明题可能出简单了。更重要的是高分的源代码会被人工抽查尤其是那些通过时间特别短、代码量特别少的提交。人工看代码的时候我最在意的是两件事。第一代码是不是“硬凑”出来的。比如有人在循环里套了无数个if去枚举特殊情况这种代码即使AC了也只能说明边界写得够多不代表抽象能力好。第二代码能不能被别人看懂。变量名是a、b、c函数几百行都不拆注释一个没有这种代码到了面试现场让本人来讲都可能讲不清楚。笔试不是终点面试官拿着你的笔试代码做追问才是很多公司的常规操作。2. 动手前的准备环境、基本功与复杂度意识2.1 笔试平台的三个隐形规则在线笔试和本地写代码完全是两回事。哪怕你刷了几百道LeetCode第一次上笔试平台还是可能被环境规则坑到。第一个规则是语言和编译版本。很多笔试平台默认支持的编译选项和最新版IDE不一样C可能只支持到C11Java可能是老版本Python也可能停留在某个中间版本。开考之前先把环境说明看清楚确认你准备用的语法特性在对应版本里能不能用。我见过有人用了C17的if constexpr特性本地编译通过线上直接编译失败整整一道题白扔。第二个规则是输入输出。在线判题机器是按测试用例来评分的每个测试用例都通过标准输入读数据再把结果写到标准输出。如果题目要求处理多个测试用例直到文件结束而你的代码只读取了一组数据就退出后面全部分数都拿不到。第三个规则是提交次数和罚时。有的平台限制每道题的提交次数比如最多十次每次提交失败都会影响最终排名。所以不要拿第一版代码就去试错先在自己本地把逻辑、边界、极端情况都测一遍再交。2.2 输入输出处理最容易翻车的环节输入输出翻车是笔试里最冤的丢分方式不是不会做而是数据根本没读进去。Java里Scanner写起来方便但在数据量大的时候性能极差会直接导致TLE。正确做法是用BufferedReader加StringTokenizer甚至手写一个快速的输入解析类。C则是cin默认要和stdio同步不关同步的话大数据输入会有明显性能损耗所以很多人一上来就写ios::sync_with_stdio(false); cin.tie(nullptr);。Python的问题也一样input()在多层循环里会拖慢速度直接改用sys.stdin.buffer.read()整体读入再解析性能要好很多。还有一个容易被忽略的点多组输入的结构。有些题目会先给一个整数T表示测试用例数量然后后面跟着T组数据有些题目不给T而是要求一直读到文件末尾。这两种写法不一样建议把两种输入模板都提前准备好考试时直接套模板把精力留给算法本身。2.3 复杂度估算落笔之前先算一笔账很多同学写代码不先算复杂度凭感觉写完一交判题机直接TLE才发现算法选错了。落笔之前先看一眼数据范围估算一下你的算法在最坏情况下要跑多少步这是程序员的基本素养。我通常按下面的参考来反推可接受的复杂度数据规模 n可接受的复杂度举例n 10O(n!)、O(2^n)暴力全排列、子集枚举n 20O(2^n)、O(n * 2^n)状态压缩动态规划n 100O(n^3)Floyd、三重循环n 1000O(n^2)双层循环、常规DPn 10^5O(n log n)排序、二分、线段树n 10^7O(n)单次线性扫描n 10^8O(log n)、O(1)数学公式、快速幂这个表格只是一个粗略参照实际还要看常数因子和平台性能但至少能做到一件事写代码之前你先知道自己这条路能不能走得通。如果数据范围是10^5你准备写O(n^2)的暴力那就不用写了大概率超时。3. 核心题型拆解研发岗最常考的4类题3.1 数组与字符串白板题的基本盘数组和字符串是出现频率最高的题型因为几乎所有公司都默认你牢牢掌握它们。这类题的核心套路其实就那几个双指针、前缀和、滑动窗口、排序加二分。双指针能解决有序数组的两数之和、三数之和、去重等问题前缀和可以把区间和查询从O(n)降到O(1)滑动窗口处理连续子数组的最长最短问题特别顺手排序加二分则是很多找满足条件的对数类题目的通用解法。我建议把这些套路整理成模板跟着同类型的题目反复练。比如最长无重复字符子串用滑动窗口、连续子数组最大和用Kadane算法、数组重叠区间合并用排序加扫描。做题的时候不要只想这题我见过而是想这题属于哪个套路。字符串问题还有一个独立考点字符集与编码。题目如果说字符串只包含小写字母那可以用数组计数如果没说用哈希表更稳妥。这种细节决定了代码的健壮性也是阅卷人非常看重的地方。3.2 动态规划从暴力递归到状态转移动态规划是校招笔试的分水岭。60%以上的压轴题最终都是DP但DP也是最难在考场上临时想清楚的。我的经验是遇到求最值方案数可行性的题优先考虑DP。想DP问题的时候按一个固定套路推进第一步定义状态dp[i]到底代表什么这个定义必须能覆盖所有子问题第二步找转移方程想清楚dp[i]怎么由前面的状态推出来第三步确定初始化和边界dp[0]是多少数组边界在哪里。如果自底向上的递推写不清楚就先写暴力递归再加一个记忆化数组改成记忆化搜索。记忆化搜索的效率往往不比循环DP差多少而且思路更直观在考场上是个保底手段。常见的DP模型——背包、最长上升子序列、最长公共子序列、区间DP——都要提前准备好模板尤其是背包问题几乎每年都有公司在笔试里考。3.3 数据结构组合拳栈、队列、哈希表有些题不考复杂的算法而是考你对数据结构的理解深度。栈的高阶用法是单调栈它能解决下一个更大元素柱状图最大矩形这类问题。队列的高阶用法是单调队列专门处理滑动窗口里的最值问题。哈希表则是空间换时间的核心工具检查存在性、去重、统计频率都离不开它。堆处理TopK问题和合并有序链表特别方便你要是不想每次手写堆也要知道语言自带的优先队列怎么用、怎么自定义比较器。刚入门的时候很多人觉得用数组模拟就行没必要特意学这些结构。但做题做到一定程度会发现结构选对了代码量和运行时间会差一个量级。比如一道题你用普通数组每次遍历找最小值复杂度O(n^2)换成单调队列/单调栈直接降到O(n)。3.4 模拟与边界题意理解能力的分水岭模拟题是另一个极端不考算法考阅读理解能力。题目给你一个复杂的规则描述让你按规则模拟整个过程。这类题最容易踩的坑不是算法难而是漏掉规则细节。比如说日历题里的闰年判断、进制转换里的负数处理、字符串题目里的空串和单字符、矩阵题目里的越界访问。我的习惯是在动手写代码之前先手动模拟一遍输入输出样例确认自己理解了规则再开始写。写的过程中把题目里每个约束条件都对应到代码的某个判断上。如果模拟完样例发现逻辑没问题再多想几个边界场景输入为空时怎么办只有一个元素时怎么办最大值和最小值同时出现时怎么办这些边界场景往往是判题机里的隐藏测试点写全了才能AC。4. 实操案例一道“满减凑单”题从读题到AC4.1 题目描述与第一步分析下面这道题是我根据当年美丽联合这类电商公司笔试的风格重构出来的一个代表性案例专门用来演示拿到一道在线编程题后的完整思考过程。某电商平台有一批参与活动的商品价格分别为prices[0], prices[1], ..., prices[n-1]每个商品最多买一件。平台设置了满减门槛m即订单总价不低于m元才能参加满减。现在需要你选出若干件商品使得总价不低于m并且在所有满足条件的方案中总价最小。如果总价最小的方案不唯一则选择使用商品数量最少的方案。返回两个整数最小总价和对应的商品数量。如果所有商品加起来都达不到门槛返回-1, -1。读完题先别急着写代码。我习惯先把题目的数学本质剥出来给定一个正整数数组求一个子集使子集和 m并且子集和尽可能小在子集和相同的方案中选元素个数最少的。这本质上是一个带约束的子集和问题可以归类到01背包变体里。这种业务包装题剥壳的功夫很关键。题目里那些满减订单门槛全部不影响算法本质。真正有用的信息就是三句话子集、最小和、最少件数。4.2 暴力解法的推导与局限最直观的想法是枚举所有子集计算每个子集的和以及元素个数然后按条件筛选。子集数量是2^n当n小于等于20的时候这个方案可行代码也很简单def brute_force(prices, m): n len(prices) best_sum float(inf) best_cnt float(inf) for mask in range(1 n): total 0 cnt 0 for i in range(n): if mask (1 i): total prices[i] cnt 1 if total m: if total best_sum or (total best_sum and cnt best_cnt): best_sum total best_cnt cnt if best_sum float(inf): return -1, -1 return best_sum, best_cnt这个解法放在小数据范围里完全没问题但笔试平台通常会有一组数据专门卡暴力比如n 100。2^100这个数量级判题机跑一百年也跑不完。所以暴力解法只能作为思路热身用来验证你对题目的理解是否正确最终还是要走优化的路。4.3 优化解法01背包思路的完整实现既然是最小化和的问题往动态规划上想就顺理成章了。我定义dp[s]表示凑出总价s所需的最少商品件数。初始化dp[0] 0其余位置设为无穷大。每遍历一个商品价格p就尝试用这个商品去更新所有可能的总价。这里要注意更新必须倒序遍历总价否则一件商品会被使用多次那就变成完全背包了。还有一个可以优化的点总价上限不需要开到所有商品的总和。因为如果某个方案的总价 m max(prices)那从这个方案里随便去掉一个商品总价会下降但仍然可能 m就算下降到m以下也会落在一个比原方案更小的总价上。所以最优解的总价一定不超过m max(prices) - 1范围内或者说我们只需要考虑m max(prices)这个上界以内的状态。这样能省不少内存。def min_items(prices, m): if not prices: return -1, -1 total sum(prices) if total m: return -1, -1 max_price max(prices) limit min(total, m max_price) # 状态空间上界 INF float(inf) dp [INF] * (limit 1) dp[0] 0 for p in prices: # 倒序遍历保证每个商品只用一次 for s in range(limit, p - 1, -1): if dp[s - p] 1 dp[s]: dp[s] dp[s - p] 1 for s in range(m, limit 1): if dp[s] ! INF: return s, dp[s] return -1, -1这段代码的时间复杂度是O(n * limit)limit不超过m max(prices)。如果题目中m和数据规模的乘积在可接受范围内就能通过全部测试点。空间复杂度是O(limit)只保留一维数组避免了二维数组的内存浪费。4.4 边界测试与在线提交代码写出来还不算完提交之前我会先在心里列出几个边界场景逐一验证。prices为空数组直接返回-1, -1。所有商品总价小于m返回-1, -1。m等于0任意一个商品都满足条件此时应该选价格最低的商品返回最小价格和1。只有一个商品且价格刚好等于m返回m和1。商品价格可能有重复DP状态下更新时会自然合并不需要额外处理。如果题目给的商品价格是小数要留意浮点误差。笔试中遇到价格尽量把单位转成分用整数运算避免精度问题。另外提交前记得删掉调试用的print。别笑每年都有人因为忘了删调试输出导致输出格式不对被判Presentation Error甚至Wrong Answer非常可惜。5. 实战避坑在线笔试里那些年踩过的坑5.1 时间超限TLE的真凶与排查我看到的线上判题表现中TLE大概是仅次于WA的第二大错误类型。它最气人的地方在于算法思路是对的就是跑不过去。常见真凶有四个。第一复杂度选高了数据范围该用O(n log n)你却写了O(n^2)。第二输入输出太慢用cin不关同步、用Scanner读十万级以上整数的时候性能差距能到几倍。第三循环内部做了无意义的高开销操作比如在循环里反复拼接字符串、反复创建对象。第四死循环while条件写错或者指针没有前进这种情况最坑代码仿佛卡住了一样。遇到TLE先把复杂度算一遍对照数据范围确认算法量级没问题再看输入输出最后看循环里有没有高开销操作。本地生成一组最大规模的数据去测一下运行时间是个很高效的办法。5.2 空间超限MLE与全局变量陷阱空间超限相比TLE出现得少一些但一旦出现往往是大数组声明不当导致的。有些人习惯开一个很大的二维数组比如int dp[1005][1005]如果不需要填满整个矩阵可以用滚动数组、一维数组或者vector来按需分配。Python里则是列表套列表稍微不注意就内存爆炸。有些语言里还有全局变量的坑。比如C全局数组在多组测试用例场景下如果上一次的测试结果没有清空下一组数据就会用到脏数据。我见过太多人在多测试用例的题里因为忘了把全局数组重新初始化WA到怀疑人生。处理这种场景干脆把数组声明在函数内部每次进入函数重新申请虽然多了一点开销但至少不会脏。5.3 边界条件空输入、单元素、极大极小的雷区边界条件是笔试判题里最经典的暗器。样例能跑通一到隐藏测试点就挂十有八九是边界没处理好。我建议在做任何一道题时都形成条件反射检查这几类边界输入为空空数组、空字符串、空列表。输入只有一个元素单元素数组或单字符字符串。数值最值int范围内的最大值、最小值long long范围内的极端情况比如乘法溢出。零和负数题目虽然声明了正整数但如果是整数没有额外说明要把0和负数情况考虑到。结果很大的取模问题不要到最后才取模运算过程中就要取模否则可能溢出。边界判断写得全不仅是为了AC也是在告诉阅卷人这个人对代码的健壮性有意识。5.4 常见问题速查表最后整理一个速查表方便你在考场上一目了然地定位问题。判题结果常见原因排查思路Wrong Answer (WA)逻辑错误、边界漏判、浮点误差打日志对比中间变量检查边界状态Time Limit Exceeded (TLE)复杂度过高、IO太慢、死循环先算复杂度再优化IO最后查循环Memory Limit Exceeded (MLE)大数组过多、递归栈过深压缩状态用迭代代替递归Runtime Error (RE)数组越界、除零、栈溢出检查下标检查除数考虑栈深度Presentation Error (PE)输出格式错误多余空格或换行对照题目输出格式逐字符检查6. 备考与临场发挥从笔试到Offer的经验之谈6.1 笔试前一个月的刷题策略如果离笔试还有一个月我不建议每天随机刷题。效率最高的方式是按专题刷。可以把常见专题分成几个阶段第一周搞定数组、字符串、链表第二周搞定树、图、搜索第三周主攻动态规划和贪心第四周用来做模拟卷和复盘错题。每天不用贪多两三道题足够了但要保证每道题都真正吃透。吃透的标准是什么我的标准是看完题目能主动说出它属于哪个专题、有哪些可能的解法、复杂度各是多少然后不看题解把它完整写出来。写完之后再看一遍有没有更优的解法。很多题目的最优解和暴力解之间可能只差一个数据结构或者一个状态定义但这个差距就是笔试分数拉开的地方。模板代码值得整理。排序、二分查找、DFS、BFS、单调栈、单调队列、背包DP、并查集这些是高频模板整理成自己的代码片段。考试时不是让你背代码而是让你省掉一些基础代码的编写时间把精力留给思考。6.2 考场上的时间分配与心态在线笔试通常给90到120分钟题量大概三到四道。最忌讳的是在一道题上死磕到底。我自己的策略是拿到卷子先把所有题都扫一遍在心里给每道题标个难度。然后从最有把握的题开始做先把能拿的分拿稳再回头啃难题。每道题卡在30分钟左右没有进展就先跳过去等所有会做的题都AC了再回来想。心态上还有一个关键点在线编程题不是竞赛不需要追求每道题都满分保证简单题和中档题的通过率通常就能进入面试轮。碰上一道完全没思路的题不要慌把你想到的暴力解法写出来能拿一部分测试点的分就拿一部分。笔试看的是综合表现一道题空着是最亏的。6.3 关于在线编程题的一些个人体会做了这么多年技术自己考过试也给别人出过题、改过卷我有一个很深的体会在线编程题真正筛选的不是谁见多识广而是谁在压力下还能保持清晰的思维。代码是要给人看的也是要运行的。你在笔试里写的每一行代码都会成为未来同事判断你是否专业的一个样本。命名规范一点逻辑清楚一点边界想全一点这些习惯不仅让你多拿分数也会在之后的工作里持续受益。最后再分享一个小技巧考前一定要去目标公司的笔试平台上做一次模拟哪怕只是熟悉一下代码编辑器的位置、输入输出的方式、提交按钮在哪里都能显著降低考场的陌生感。很多翻车不是输在实力上而是输在第一次用不熟悉的平台。把这些细节都准备好把该踩的坑提前踩一遍真正的笔试就会变得从容很多。