
2015年秋天我参加了凤凰网的校园招聘研发工程师笔试。那会儿正值秋招高峰期手里已经拿了几个中小厂的offer但凤凰网的笔试还是让我印象深刻——不是因为题有多难而是它的题目设置非常“实”考察的全是日常开发中真正用得上的基本功几乎没有偏题怪题。当时我在笔试现场就觉得这套卷子出得挺有水平后来和几个同样参加过这场笔试的同学复盘大家的感受也一致凤凰网这场笔试本质上不是筛智商而是筛“有没有认真写过代码”的人。这篇文章就来完整拆解一下这场笔试的题型结构、核心考点和对应的解题思路同时结合我自己的备考和实战经验给准备参加技术类校招笔试的同学一份可以“抄作业”的复习指南。不管你是即将参加笔试的应届生还是刚入行想补基本功的初级工程师这篇文章都值得认真读一遍。1. 笔试整体设计与考点分布1.1 出题思路为什么凤凰网要这样考凤凰网作为一家以内容资讯为核心的互联网公司它的研发团队日常面对的场景是高并发访问、海量内容数据、多端适配、快速迭代。因此它招聘研发工程师时并不需要一个只会背八股文的“理论家”而是需要一个能直接上手写业务、处理真实问题的“实战派”。从笔试题型分布就能看出这一点。整张卷子大致分为四块专业基础选择题、算法与数据结构编程题、系统设计与逻辑题、简答与开放性论述题。前两块占比最大后两块虽然分值不高但往往是区分度最高的部分。注意不同年份、不同岗位比如Java开发、C开发、前端开发的笔试题型会有差异但整体考察逻辑是一致的——基础是否扎实、编码能力是否过关、思路是否清晰。1.2 题型结构与时间分配策略我这里根据当时笔试的回忆和同期小伙伴的反馈整理了一份大致的题型分布表题型大致题量建议用时考察重点选择题含多选15-20题30分钟数据结构、操作系统、网络、数据库基础填空题/简答题5-8题20分钟语言特性、SQL语句、概念理解编程题3-5题60分钟算法设计、编码实现、边界处理逻辑与系统设计题1-2题20分钟逻辑思维、架构意识、表达能力开放性论述题1题10分钟技术视野、学习能力、价值观整套笔试时间一般控制在2小时左右题量不算少所以时间分配特别关键。我当时的策略是选择题不超过30分钟拿不准的先标记跳过绝不在一道选择题上耗超过2分钟编程题优先做自己最有把握的那道先拿稳基础分再啃难题系统设计和论述题放在最后因为这类题没有绝对的对错只要写出思路就有分。这里想多说一句很多同学在笔试时容易犯一个错误——在一道选择题上死磕。比如一道关于TCP状态转移的题四个选项都看着对结果一纠结就是五分钟。这在2小时的笔试里是非常奢侈的。正确做法是相信第一直觉先选上用标记功能记下来等所有题目做完后如果有剩余时间再回头检查。2. 核心考点深度拆解2.1 数据结构与算法笔试的重头戏凤凰网这场笔试的算法题难度属于中等偏上没有到ACM竞赛那种程度但也不是随随便便就能写出来的。我印象最深的有几类题目链表相关操作、二叉树遍历、字符串处理、排序与查找的变种。先说链表。有一道题是“判断一个链表是否有环并找出环的入口节点”。这道题算是经典中的经典了考察的是快慢指针的思想。常规解法是定义两个指针一个每次走一步一个每次走两步如果两者相遇说明有环。至于找环的入口需要再推导一步从相遇点到环入口的距离等于从链表头部到环入口的距离所以让一个指针从头开始另一个从相遇点开始都每次走一步再次相遇的位置就是环入口。// 判断链表是否有环并返回环入口节点 ListNode* detectCycle(ListNode* head) { ListNode *slow head, *fast head; while (fast ! NULL fast-next ! NULL) { slow slow-next; fast fast-next-next; if (slow fast) { // 有环找入口 ListNode *p head; while (p ! slow) { p p-next; slow slow-next; } return p; } } return NULL; }这道题能考出很多东西你知不知道快慢指针这个技巧、你能不能推导出环入口的位置关系、你的代码有没有处理空指针和边界条件。我当时在笔试时写这道题专门在注释里写清楚了两指针相遇时的数学关系后来复盘时觉得这个小细节是可以加分的。再说二叉树。凤凰网的笔试里出现了一道“求二叉树最大深度的变种题”——不光是求深度还要输出从根节点到最深叶子节点的路径。这题其实考的是递归加回溯。求深度本身很简单但路径输出需要你在递归过程中维护一个路径数组走到叶子节点时判断是否比当前最长路径更长是的话就复制保存。用C写大概长这样void findLongestPath(TreeNode* node, vectorint path, vectorint result, int maxDepth) { if (node NULL) return; path.push_back(node-val); if (node-left NULL node-right NULL) { if ((int)path.size() maxDepth) { maxDepth path.size(); result path; } } else { findLongestPath(node-left, path, result, maxDepth); findLongestPath(node-right, path, result, maxDepth); } path.pop_back(); // 回溯 }这道题的关键在于理解递归栈和回溯的关系path.pop_back()这行不能漏否则路径就会带着不该出现的节点。字符串处理方面凤凰网考了一道“统计字符串中出现次数最多的字符及其次数”属于送分题但有的人用哈希表有的人用两层循环代码复杂度天差地别。用哈希表是O(n)两层循环是O(n²)。在笔试现场能写出O(n)解法的同学自然会拿到更高的评价。2.2 操作系统与网络基础概念不能只背不练选择题部分有不少操作系统和计算机网络的题。操作系统考察集中在进程与线程的区别、死锁产生的四个必要条件、进程间通信方式、内存管理的基本概念等。网络部分则集中在TCP/IP协议栈、TCP三次握手与四次挥手、HTTP协议状态码、DNS解析过程这些高频考点上。这里我要特别强调一个经验这些基础概念光靠背是记不牢的。我备考的时候有个习惯每复习一个协议或者概念就想想“这个知识我在写代码的时候有没有用到过”。比如TCP三次握手你不光要知道它解决的是“确认双方收发能力”的问题还要能说清楚为什么是三次而不是两次——因为如果只有两次握手服务端无法确认客户端的接收能力是否正常。这样想一遍记忆就深刻多了。再比如HTTP状态码很多同学只记了200、404、500这三个。但凤凰网笔试里有一道题问“301和302的区别”这题就有点意思了。301是永久重定向302是临时重定向这个区别直接关系到搜索引擎的SEO处理和浏览器的缓存行为。如果只是背定义而不理解它们的应用场景遇到这种题就容易翻车。数据库这块考了一道经典的SQL题——查出一个表中每个部门工资最高的员工信息。这题考察GROUP BY和子查询的配合以及JOIN的用法。如果你知道窗口函数ROW_NUMBER()的写法还能多写一种解法显得你技术视野比较广。-- 每个部门工资最高的员工 SELECT e.* FROM employee e JOIN ( SELECT department_id, MAX(salary) AS max_salary FROM employee GROUP BY department_id ) d ON e.department_id d.department_id AND e.salary d.max_salary;2.3 编程语言特性选对工具事半功倍凤凰网的技术栈以PHP为主但也招聘Java、C方向的研发工程师所以笔试中语言题目会根据你投递的方向有所不同。我投的是Java方向编程题允许用Java写但语言特性相关的题也出了好几道。有一道填空题是“final、finally、finalize 三者的区别”这道题算Java基础里的经典题了。final是修饰符可以修饰类、方法、变量finally是异常处理的关键字用于定义一定会执行的代码块finalize是Object类的一个方法在垃圾回收时被调用。如果你只是背下来这三句话可能不够面试官其实想听的是“你有没有在实际开发中用过finally来释放资源”以及“为什么现在不推荐依赖finalize来做资源清理”。还有一道选择题涉及Java中HashMap的底层实现原理。2015年那会儿Java 8已经发布了HashMap在数据量大时会从链表转为红黑树这个知识点在笔试中出现得很及时。如果你只是看过《Java编程思想》而没看JDK源码这道题估计只能靠猜。所以说阅读源码的习惯不是可有可无的它在笔试里真的能拉开差距。C方向的考生则需要注意指针和引用的区别、深拷贝和浅拷贝、虚函数和纯虚函数这些考点。这些语言特性层面的题目考察的不是你能不能用这个语言写出“Hello World”而是你写代码时知不知道自己写的每一行背后发生了什么。这就是笔试和面试官判断候选人是否“靠谱”的重要依据。3. 实操过程与解题思路还原3.1 一道典型编程题的完整解拆我在笔试中遇到的一道比较有代表性的编程题是这样的给定一个无序整数数组找出其中没有出现的最小正整数。要求时间复杂度O(n)空间复杂度O(1)。这道题当年在LeetCode上是原题First Missing Positive但在笔试现场完全靠记忆写出来还是有点压力的。它的核心思路是“桶排序思想”——把每个正整数放到以它减一为下标的位置上然后再遍历一遍找出第一个位置下标和值不匹配的。public int firstMissingPositive(int[] nums) { int n nums.length; // 第一遍将每个数放到它应该去的位置 for (int i 0; i n; i) { while (nums[i] 0 nums[i] n nums[nums[i] - 1] ! nums[i]) { int temp nums[nums[i] - 1]; nums[nums[i] - 1] nums[i]; nums[i] temp; } } // 第二遍找第一个缺失的正整数 for (int i 0; i n; i) { if (nums[i] ! i 1) { return i 1; } } return n 1; }这道题有意思的地方在于它考察的不只是你会不会排序而是你能不能跳出“排序再查找”的惯性思维。它要求O(n)时间复杂度和O(1)空间复杂度意味着你不能用额外数组也不能用排序算法。这种“带着镣铐跳舞”的题目最能考验一个工程师的逻辑思维和代码功底。我当时在笔试中顺利写出了这个解法而且特意在注释里写了“利用了数组下标与值之间的一一对应关系”这等于告诉阅卷人“我知道我在做什么不是背的答案”。事实证明这个习惯在后来的面试中也帮了我很多。3.2 SQL题与数据库设计题的实操演练凤凰网笔试的数据库题除了前面提到的“每个部门工资最高的员工”还有一道建表题。题目要求设计一个简单的用户订单表并写出查询“最近一个月内下单金额超过1000元的用户”的SQL。这题看似简单其实背后考察的是表结构设计的基本功。很多同学上来就写CREATE TABLE但忽略了一些关键点金额字段用什么类型、时间字段用什么类型、要不要建索引。我当时在答题时特意说明了自己的设计理由——金额用DECIMAL(10,2)而不是FLOAT是为了避免浮点精度误差时间用DATETIME而不是VARCHAR是为了能直接进行时间范围比较。查询SQL我写的是SELECT u.user_id, u.user_name, SUM(o.order_amount) AS total_amount FROM user u JOIN orders o ON u.user_id o.user_id WHERE o.order_time DATE_SUB(NOW(), INTERVAL 1 MONTH) GROUP BY u.user_id, u.user_name HAVING total_amount 1000;这道题如果用我们现在的眼光看其实还有优化空间比如考虑索引设计、分页方案、或者改用窗口函数。但在2015年的校招笔试中能把JOIN、GROUP BY、HAVING串联起来用对已经能超越一半以上的考生了。3.3 系统设计题的应对思路凤凰网的笔试最后有一道系统设计题原题大意是设计一个短链接服务要求支持高并发访问说明你的整体架构和数据存储方案。这题当时拿到手我就知道它不是考标准答案而是考你的工程思维。我把答题分为四块写整体架构、数据存储、重定向流程、并发优化措施。架构层面使用“前端负载均衡 应用层无状态服务 后端存储”这样的经典分层存储层面使用关系型数据库存储短码与原始URL的映射关系并利用缓存层承担热点数据的读取重定向流程则描述了一个请求从进入Nginx到最终302跳转的全过程。这类题目阅卷人不是要你设计出一个能上生产的完美系统而是看你有没有分层架构的意识、有没有考虑过缓存和并发问题、有没有区分存储与计算。换句话说它考察的是你的“工程师嗅觉”。即使你描述的方案存在瑕疵但只要思考维度是全面的分数就不会低。4. 备考策略与常见问题排查4.1 校招笔试备考的时间规划不少人觉得笔试能力是突击出来的考前刷两周题就够了。我的亲身经历告诉我刷题是必要的但突击只能让你从“完全不会”变成“会做简单题”对中高难度的算法题几乎无用。真正稳妥的备考方案是长期积累加考前冲刺的组合拳。长期积累贯穿整个大学/研究生阶段数据结构、操作系统、计算机网络、数据库这四门课是核心中的核心。上课认真听作业自己写不要抄。平时可以逛逛技术社区看看一些底层原理类的文章保持对工程技术的敏感度。考前冲刺笔试前2-4周集中刷题重点刷LeetCode上的高频题和经典题。每天至少3道并且要按专题刷——链表刷完刷二叉树二叉树刷完刷动态规划。每道题写完后看最优解法不要满足于“能跑就行”。实战模拟笔试前1周找几套其他公司的校招真题严格按2小时时间限制做一遍。这样做的好处是训练时间分配能力和考场心态避免到了真实笔试时因为紧张而发挥失常。4.2 备考中的典型弯路和常见坑第一个坑是“只看不写”。很多同学复习算法的时候喜欢看题解看的时候觉得“哦我懂了”但合上书自己写的时候却容易卡壳。这是典型的眼高手低。笔试题最终是要在白纸/编辑器里写出来的眼睛会了不代表手会了。我备考时会强迫自己每道题都完整敲一遍代码跑通测试用例才算过。第二个坑是“忽视边界条件”。我见过不少同学写代码时主逻辑很顺但一遇到空指针、数组越界、字符串为空这些情况就直接崩溃。笔试阅卷时会专门看有没有处理边界条件。我的习惯是写完代码后自己构造几组特殊输入测试一下空数组、只有1个元素的数组、全是负数的数组、有重复元素的数组。把这些情况都测一遍代码的健壮性就上来了。第三个坑是“心态崩盘”。笔试现场总会有几道题是你不会或不确定的这非常正常。关键是不能因为一道题卡住就乱了阵脚。我的经验是遇到不会的题先跳过做后面的题等全部做完再回头攻克。即使最终没做出来也要写一部分思路和伪代码上去让阅卷人看到你的思考过程。留空白是零分写思路至少还能拿到过程分。4.3 笔试现场的答题技巧与细节管理笔试不只是知识储备的比拼也是应试技巧的比拼。几个实用的小技巧分享给大家先通览全卷。开考后用1分钟把整张卷子翻一遍了解题型分布和题目数量心里有个全局规划。特别留意编程题的分值如果编程题占了大头前面的选择题就要加快速度。答题顺序有讲究。我的原则是“先易后难、先高性价比后低性价比”。选择题和填空题虽然分值不高但耗时短、容易拿分应该放在前面做编程题分值大需要整块时间放在中间做最后留给系统设计题和论述题。注意书写规范和代码排版。手写代码时字迹要清楚缩进要规范变量命名要有意义。阅卷人一天要看几十份卷子一份排版清晰、注释得当的答卷在主观印象上就赢了。不确定的题也要写点什么。选择题如果实在不会先排除明显错误的选项再猜正确率能提升不少。简答题就算不知道精确答案把你理解的相关概念写在上面也能拿到部分分数。5. 笔试之后复盘的价值不亚于刷题5.1 从凤凰网笔试中沉淀下来的知识清单笔试结束不等于事情结束了。我每场笔试结束后都会做一次系统复盘把不会的题、犹豫的题、做错了的题全部整理到一份专门的笔记里。这份笔记在后续面试中发挥的作用比我再刷100道题都大。从凤凰网这场笔试中我沉淀下来的知识点包括快慢指针在链表问题中的应用、二叉树递归与回溯的配合、HashMap底层红黑树的转换条件、TCP三次握手与四次挥手的完整状态转移、SQL中GROUP BY与HAVING的执行顺序、以及系统设计题中的分层架构思想。这些知识点在后来的面试中被反复问到可以说“一场笔试受益整个秋招”。5.2 如何把笔试经验转化为面试优势笔试和面试是校招的两个环节但它们的备考是连贯的。笔试题里考的知识点面试中大概率会以“聊一聊”的形式再次出现。比如你在笔试题里写了快慢指针找链表环入口面试官就可能在面试中问“你了解快慢指针的原理吗为什么快指针走两步而不是走三步”这时候如果你能不仅写出代码还能讲清楚数学推导过程会给面试官留下非常深刻的印象。所以建议大家在准备笔试时不要只满足于“AC了”还要理解每一道题背后的原理。把一道题吃透比囫囵吞枣做十道题的收获大得多。另一个实用技巧是把你笔试中做过的有代表性的题目整理成一篇技术笔记或博客。一方面这能帮助自己梳理思路另一方面在面试中如果被问到“你最近在学什么”你可以很自然地拿出自己的笔记展示。这比说“我在刷题”显得有深度得多。6. 写给正在准备校招的你一些掏心窝的话6.1 基础永远是最重要的凤凰网这场笔试让我印象最深的不是哪道题很难而是它几乎把所有重要的基础知识点都覆盖了一遍。数据结构、算法、操作系统、网络、数据库、编程语言样样都考。这其实反映了一个朴素的道理无论技术栈怎么变化无论什么级别的工程师基础都是立身之本。在校招这个阶段你没有多少项目经验可以展示笔试和面试官唯一能考察的就是你的基础知识和学习能力。所以不要浮躁地追逐热点技术先把数据结构这门课吃透把操作系统和网络的核心概念理解清楚把一门编程语言用熟练。这些基础性的东西才是你在校招中真正竞争力的来源。6.2 笔试的终点是新的起点我最后通过凤凰网的笔试进入了面试环节虽然最终没有选择去那里但这场笔试对我的技术成长起到了很大的推动作用。它让我看清了自己在哪些方面还存在知识盲区也让我学会了如何在压力下快速思考和组织答案。所以我想说的是不要害怕笔试把它当成一次检验自己的机会。就算某场笔试挂了只要你认真复盘了下一场笔试你一定比上一场更强。校招是一场马拉松不是短跑。一次的失利不代表什么持续进步才是关键。最后再分享一个小技巧当年我在每一场笔试的草稿纸角落都会写一行提醒——“仔细读题注意边界先易后难”。这几个字虽然简单但在2小时的紧张答题中确实帮助我避免了很多低级失误。你也可以找一个属于自己的“考场暗号”在紧张的时候看一眼让自己冷静下来。祝大家都能在校招中拿到心仪的offer。