
1. 为什么一张2013年的笔试题还能让我反复拿出来讲1.1 那一年美团在干什么2013年的美团正处于“千团大战”的收尾阶段也是从团购向本地生活服务纵深拓展的关键节点。当时的技术团队规模远没有今天这么庞大整个后端要支撑的却是全国上百个城市的商家、用户、订单、团购券核销等核心链路。只要一到饭点和节假日流量就会瞬间冲上来线上问题频发是常态。这种背景下研发笔试的命题人非常务实他们不想招一个只会背八股文的人而是想招一个能直接上手处理线上问题、能理解业务复杂度的人。“美团2013研发笔试卷”这几个字表面上是一份老古董实际上是一份相当典型的互联网公司基础能力测试样本。它的考察范围没有现在那么多花哨的框架、中间件、分布式理论反而更集中在数据结构、算法、操作系统、网络、数据库这些计算机基本功上。现在很多同学刷题动辄就是LeetCode Hard却忽略了这些底层能力的系统化梳理回过头看这份卷子反而像一面镜子能照出基本功的薄弱点。1.2 这套卷子考察的底层能力我把这套卷子翻来覆去看了很多遍也和当年参加过的同事对过题目它主要考察五个方向编程语言基础、数据结构与算法、操作系统与网络、数据库设计、业务场景方案设计。这五个方向背后对应的是一个研发同学日常工作中最常打交道的几件事写代码、调接口、查日志、设计表、扛并发。这里要说明一下美团是2010年成立的2013年时它的技术体系还在快速演进中。当时的笔试卷并没有像现在这样分前端、后端、算法、数据等细分的岗位卷而是以“研发工程师”统一命题。这就意味着试卷题目不会特别偏门大部分是经典的、值得反复咀嚼的题目比如链表反转、字符串匹配、二叉树遍历、TopK、系统设计等。正因为这些题足够经典所以放到今天的校招笔试里也一点不过时。1.3 谁适合拿这套题来练手如果你是准备校招或跳槽的Java、Go、C方向研发这套题可以作为第一轮基本功自测如果你是刚入行一两年的同学想系统补一下计算机基础这套题也能帮你画出复习地图就算你已经在做业务开发偶尔回头看这些基础题也会发现很多线上问题其实都能追溯到这些底层原理比如数据库死锁、缓存雪崩、连接池耗尽、消息乱序等。我之所以愿意专门写一篇文章来拆解这份试卷是因为它代表了一个阶段的互联网研发面试风格也因为它考察的内容能让你少走弯路直接抓住面试准备的核心。2. 题目的整体结构与考点拆解2.1 选择题基础知识的“照妖镜”2013年这类互联网公司笔试卷的第一部分通常是选择题题量在20到30道之间。考察范围非常固定C语言或Java的语言细节、数据结构复杂度、操作系统进程线程、TCP/IP协议、Linux常用命令、数据库索引与事务。以C语言为例常见考点是指针与数组的关系、内存分配与释放、宏定义与函数的区别、结构体对齐等。很多同学觉得这些东西太底层了日常工作用不到但实际上内存越界、栈溢出、指针空引用这些问题在C/C后端服务里仍然是线上事故的主要来源。美团当时有大量的后端服务是Java写的但笔试也考C语言基础这是为了考察候选人对计算机底层原理的理解深度。Java方向的考点则是String、HashMap、多线程、异常处理、JVM内存区域划分等。其中HashMap几乎是必考因为它在JDK 7和JDK 8中的实现差异很大涉及哈希冲突、扩容、红黑树等知识点可以很自然地延伸出并发安全、线程安全容器等一系列问题。我建议复习时不要死记结论而是要把源码翻出来读一遍搞清楚为什么HashMap不是线程安全的ConcurrentHashMap又是通过什么手段保证线程安全的。网络部分的经典考点包括TCP三次握手与四次挥手、TIME_WAIT状态、HTTP与HTTPS的差异、GET与POST的区别、Cookie与Session的区别等。这些概念看似简单但面试官特别容易从笔试题延伸到追问比如“为什么TIME_WAIT要等待2MSL”“HTTPS握手过程到底交换了什么”。如果笔试阶段只是模糊地知道一个大概很容易在后面的面试环节露馅。2.2 编程题面试官想看的不是答案而是思路编程题是整张卷子的重头戏通常有2到3道分值占比最高。2013年那会儿在线OJ系统还不像现在这么普及很多笔试还是纸质答卷要求手写代码。这种情况下书写格式、变量命名、逻辑清晰度都会被放大观察。面试官想通过纸质代码看到的是这个人的编码习惯好不好、边界条件考虑得全不全、时间复杂度有没有分析清楚。从题目类型来看出现频率最高的是这五类数组与字符串处理、链表操作、二叉树遍历与重建、排序与查找、递归与动态规划入门题。举几个当时的高频题目原型反转链表包括单链表反转和K个一组反转判断一个字符串是否为回文串进阶版是找出最长回文子串给定两个有序数组求合并后的中位数手写快速排序或堆排序并分析最坏时间复杂度二叉树的前序、中序、后序遍历要求至少会写递归版本优秀候选人能写迭代版本。很多人觉得这些题太老了没有区分度。但实际上越基础的题越能区分出“背过答案”和“真正理解”两种候选人。比如反转链表70%的人能写出三指针迭代版但只有一小部分人能清楚地解释递归版每一层调用栈的状态变化。面试官只要顺着递归版本问一句“递归深度是多少”“栈溢出怎么办”高下立判。2.3 设计题把“线上问题”搬到纸面上设计题是美团这类业务驱动型公司比较有特色的部分。考的往往不是如何设计一个高可用的分布式系统而是更贴近当时业务的具体问题。比如“商家在某平台上线一个团购套餐用户购买后到店消费整个流程涉及哪些状态如何设计数据库表结构”“团购App首页的信息流是如何生成的如何优化响应速度”“用户下单后支付超时怎么处理这笔订单”等。这类题目没有标准答案但考察的是候选人有没有做过真实业务有没有状态机思维能不能画出核心流程。以团购订单状态为例一个完整的订单至少包含已创建、待支付、已支付、已消费、已退款、已过期等状态。每种状态之间有哪些合法流转哪些流转需要并发控制哪些流转要发消息通知都是在设计时要明确回答的内容。我在做这类题时总结出一个套路先画出核心实体与关系再列出核心状态机然后补充分布式或并发相关的问题点最后能给出一个可落地的接口或表结构设计。按这个顺序答题即使方案有瑕疵面试官也能看出你有完整的思考链路。3. 几道典型真题的完整推演3.1 一道经典的“两个有序数组找中位数”先说题目原型给定两个大小为m和n的有序数组nums1和nums2找出它们合并后的中位数要求时间复杂度为O(log(mn))。这是后来LeetCode上的Hard题但2013年它就已经是很多公司笔试的压轴题了。这道题能考察的东西非常多二分思想、边界处理、递归与循环的转换、时间复杂度的严格推导。我当年第一次做这道题时第一反应是先把两个数组合并然后直接取中间位置时间复杂度O(mn)空间复杂度O(mn)。这个答案在笔试中只能拿一半分因为要求写的是O(log(mn))O(mn)只是暴力解。后来我花了一个周末反复推演才彻底理解最优解的核心思路中位数本质上是“把所有元素分成左右两堆且两堆数量差不大于1并且左堆最大值不大于右堆最小值”。用二分法在较短的数组上切一刀通过数学关系推导出另一个数组上的切分位置然后比较边界值即可。这里的关键难点不是算法本身而是边界条件非常多比如某个数组为空、切分位置在0或末尾、mn为奇数或偶数等。为了彻底搞定这道题我建议你亲手在纸上把两个数组的相对位置图画出来反复走三条用例一长一短、等长、包含负数或相同元素。这道题放到现在依然是高频题但它带给我的启发已经超越了题目本身面对复杂问题时不要急着写代码先用“分治思想”拆解子问题再逐一处理边界这才是系统化解题的正确姿势。3.2 一个真实的考察点手写LRU缓存LRULeast Recently Used缓存在2013年的笔试卷里出现频率就很高现在更是后端面试的常客。它的要求很明确设计一个数据结构支持get(key)和put(key, value)两个操作且两者的平均时间复杂度都是O(1)当缓存容量达到上限时淘汰最久未被使用的数据。当时很多人第一时间想到的是用LinkedHashMap在Java里几行代码就能搞定。但笔试通常要求在没有现成类的条件下自己用“哈希表双向链表”实现或者至少画出结构图、说明put和get的完整流程。这就把只会用工具类和理解底层原理的人区分开了。双向链表的作用是维护数据的使用顺序哈希表的作用是通过key直接定位到链表节点。get命中时把节点从当前位置移到链表头部put新增时先在哈希表里查如果已存在则更新值并移动到头部如果不存在则插入头部再判断是否超过容量超过就删除链表尾部的节点并同步删除哈希表中的记录。所有操作都只是指针的移动和哈希表的读写所以O(1)是可以保证的。我在面试别人的时候特别喜欢追问两个问题一是为什么用双向链表而不是单向链表二是并发场景下怎么保证线程安全。第一个问题的答案很直接删除某个节点时需要同时知道它的前驱节点单向链表做不到O(1)删除。第二个问题的答案则可以选择加全局锁或者用ConcurrentHashMap配合锁分段又或者直接使用Java现成的LinkedHashMap加同步包装。能答到第二层的候选人基本已经超过了80%的面试者。3.3 怎么答好“短URL系统设计”还有一类高频设计题就是“设计一个短URL系统”。这道题在2013年美团笔试里出现过变体核心是给定一个长URL生成一个尽量短的唯一标识访问短URL时能重定向到原始长URL。看起来简单但展开后覆盖的考点非常多哈希算法选择、唯一ID生成、存储表设计、重定向方式、缓存策略、过期策略、并发安全、防攻击等。我推荐的答题框架是这样先用一个自增ID作为主键再用这个ID做62进制转换生成短码。这样生成的短码可以保证唯一性而且通过取模分库分表以后仍然可以反向解析。访问时拿到短码先查缓存缓存未命中再查数据库查到后拼好完整URL返回302重定向。这里有一个容易被忽略的细节重定向应该用302还是301。301表示永久重定向浏览器会缓存后续请求不再打到短URL服务统计点击量会不准302表示临时重定向每次请求都会经过服务端方便做访问统计、安全校验和灰度控制。所以追求数据准确性的系统一般选302。这道题还经常伴随追问如果多个长URL都想生成同一个短码怎么办答案通常是加一个“长URL到短码”的唯一索引重复请求直接返回已有短码。如果短码被恶意遍历怎么办答案通常是增加校验位或者限制单位时间内的访问频率。这一连串追问下来考察的其实是一个人对完整业务链路的掌控力。4. 从2013到现在的技术演进笔试为什么还在考这些4.1 当年的技术栈LAMP、Java、MySQL是标配2013年互联网后端的主流技术栈基本是LAMPLinux、Apache、MySQL、PHP和Java两大阵营。美团早期很多业务是PHP写的后来随着业务复杂度上升逐步往Java服务化方向迁移。所以当年的笔试题里Java和PHP都可能出现数据库则以MySQL为主NoSQL领域当时Memcached和Redis已经有了不少应用。那时候的架构远没有今天这么复杂。很多系统是单机部署加上读写分离缓存用来抗峰值流量消息队列用得还比较少微服务更是没有影子的事情。但正因为系统简单笔试更看重一个候选人能不能把单机上的问题处理明白内存够不够、数据库连接池多大、慢查询怎么优化、缓存和数据库的一致性怎么保证。这些基础能力在今天依然没有过时。我经常对团队里的新人说一句话你可以在工作中不直接写操作系统代码但你必须知道进程和线程在什么情况下会阻塞必须在线上CPU飙升时能快速想到是GC问题还是死循环问题。这些能力不会因为框架迭代而贬值。4.2 今天的变化微服务、容器化、云原生但基础不变现在的技术栈确实变了很多。Docker和Kubernetes成为标配服务治理框架从Dubbo到Spring Cloud再到各种云原生组件MySQL之上加了一层又一层中间件Redis也从一个缓存工具成长为一个数据生态。很多人会问既然变化这么快为什么校招笔试还死磕这些老题我的观点是基础题目考察的不是某个工具怎么用而是一个人的抽象能力和逻辑能力。比如你懂二分查找就更容易理解分库分表的路由规则你懂哈希表原理就更容易理解分布式缓存的分片和一致性哈希你懂TCP握手状态迁移就更容易理解为什么RPC框架要设计心跳和重连机制。上层框架会变但底层原理是稳定的这就是经典题目经久不衰的原因。这几年还有一个趋势就是笔试题目开始结合公司具体场景比如给出一段线上慢查询日志让候选人分析问题原因或者给出一个实际发生的缓存穿透故障让候选人设计方案解决。这类题目的底层仍然是在考基础知识只不过换了一个更贴近生产环境的包装。备考的同学不要只盯着算法题刷也要花时间读一读常见中间件的官方文档和源码解析。4.3 从热搜词看技术体系的演进接口安全与风控设计值得重视最近总看到一些技术讨论里出现美团相关的基础设施关键词比如签名机制、参数加密、网关防刷之类的。这些词背后其实是互联网公司普遍面临的接口安全挑战如何确认请求来自真实用户如何防止脚本批量刷接口如何在服务端做参数合法性校验。这不是某一家公司特有的问题而是所有线上业务都会遇到的基础工程问题。从研发角度来理解设计一个安全的接口体系通常包含几个层面第一是身份认证确认调用者是谁第二是参数签名防止请求内容在传输过程中被篡改第三是频率限制防止单IP或单用户在短时间内的异常请求第四是数据加密保护敏感字段不出现在明文日志里。理解这些通用机制对研发人员来说是基本功而不是什么黑魔法。我特别想提醒一点上述所有机制的实现都有成熟的行业标准和开源方案正常研发工作只需要基于规范去实现完全没有必要去研究任何绕过手段。作为技术人员把精力花在如何让系统更健壮、更安全上才是正向的成长路径。5. 备战这类笔试的实战经验与避坑指南5.1 时间分配与做题顺序如果还原2013年的笔试场景通常是一场60到90分钟的闭卷考试。时间紧、题量大所以做题顺序直接决定最终得分。我的建议是先做选择题遇到不会的快速标记跳过不要在一道题上卡超过2分钟然后做编程题里的前两题先把稳妥的解法写出来拿分数最后留出20分钟处理设计题和压轴题。编程题有个经验法则如果一开始只能想到暴力解法先把暴力解法写清楚并注明时间复杂度和空间复杂度然后在剩余时间里尝试优化。笔试卷子上留白比写错更致命因为至少写了暴力解就能拿部分分而空白就意味着零分。还有一点是书写规范。纸质笔试时代码不要写得太挤变量命名要有意义循环和递归的终止条件写清楚。哪怕题目没要求也可以顺手写一段注释说明核心思路。面试官阅卷时第一眼看的是整体卷面第二眼才是答案本身一个良好的卷面能让你在主观题上多不少印象分。5.2 高频丢分点边界条件、复杂度分析、状态流转结合我自己的踩坑经历和帮人改简历时看到的笔试复盘大部分人的丢分点集中在三个地方。第一个是边界条件。比如二分查找的左右指针相遇条件、数组长度为空或为1的情况、链表只有单个节点的情况、整数溢出问题。这些问题往往不是不会做而是做题时太急没把用例在纸上推演一遍。我的方法是写完代码后立刻构造三组用例——空数据、单元素、正常多元素——在脑子里逐行执行一遍。这个方法很笨但非常有效。第二个是复杂度分析缺失。很多候选人能把算法想出来却写不出复杂度推导过程。笔试要求“分析时间复杂度”时一定不要只写一个结论要把递推式或循环次数说明白。比如分析快排时最好能说明最坏情况是O(n²)、平均情况是O(nlogn)并解释最坏情况什么时候出现。第三个是状态流转不完整。设计题中涉及订单、支付、任务等有状态的实体时状态枚举之间要满足“完整且互斥”就是任何一个状态都要有定义任意两个状态之间是否可流转要有明确结论。很多人会在“已取消”和“已退款”之间含糊不清这会让面试官怀疑你没有真正经历过线上业务。5.3 系统设计题的通用回答框架关于设计题我在前面提到了一个五步框架这里展开说一下第一步明确需求边界。确认要设计的系统核心功能是什么哪些是MVP阶段必须实现的哪些可以后续扩展。比如短URL系统最核心的就是短码生成和重定向数据统计分析可以往后放。第二步抽象核心实体和关系。不管是订单、用户、商家还是URL先画清楚实体与实体之间的关系定义好主键、外键和唯一约束。第三步设计核心流程和状态机。把一个操作的完整生命周期讲清楚包含每一步的输入、输出、异常分支。第四步识别瓶颈与风险。这是区分经验深浅的关键环节。比如高并发写入时数据库是否扛得住重复请求怎么幂等处理缓存热点怎么解决数据一致性用什么方案保证。第五步用“演进”视角表达方案。说明当前方案满足业务现状同时给未来留出升级空间比如先单库后分库先本地缓存后分布式缓存。这种表述会让面试官觉得你是一个有全局观的工程师而不是只会实现需求。这套框架不仅适用于笔试也适用于日常技术方案评审。我把这五步打印出来贴在工位上每次做设计文档时先按这个顺序过一遍能明显减少返工次数。写在最后这套老试卷给我的三个启发第一技术是有“保质期”的但基础没有。2013年流行的框架到了今天大多数已经变了样但数据结构、操作系统、网络协议、数据库事务这些底层知识依然是研发同学解决新问题的支点。如果把技术栈比作树的枝叶那基础能力就是树干树干足够粗壮换什么框架都不怕。第二一份好的笔试卷本质是在模拟真实工作场景。选择题考察的是日常工作里踩坑后沉淀下来的知识点编程题考察的是把思路翻译成代码的能力设计题考察的是面对模糊需求时的拆解能力。认真复盘一套老题比盲目刷一百道同类型的新题更有价值。第三准备面试这件事最大的收益不是拿到Offer而是逼着自己把知识体系重新梳理一遍。我当年为了准备这套笔试花了三周把《数据结构与算法》《深入理解计算机系统》重新读了一遍很多当时不太理解的概念都豁然开朗。这种系统性的梳理对后续几年的工作都有帮助。如果你手上也有类似的旧试卷不管来自哪家公司都建议不要只看答案而是把每一道题背后涉及的知识点列成一张清单逐个击破。等你把这张清单上的东西都吃透了你会发现工作的很多难题本质上还是这些老问题的变种。