尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

研发岗笔试如何准备?高频考点与经典编程题全解析

研发岗笔试如何准备?高频考点与经典编程题全解析 又到一年求职季后台好几个学弟学妹问研发岗笔试到底怎么准备。我翻出自己存了很多年的一份PDF文件名写着“人人网2015研发笔试卷B”。说实话这家产品的名字现在提起的人不多了但这套卷子我一直没舍得删。它的出题风格非常典型难度适中但覆盖面广很适合作为研发岗笔试的摸底自测。无论你投的是后端、客户端还是算法岗拿这套题过一遍基本能看出自己的知识体系哪里还有洞。这篇文章不打算做“对应聘者通用的废话建议”而是把我自己刷这套卷子以及在真实面试中作为面试官看到过的高频错误一次性讲清楚。内容包括这类卷子的出题逻辑、高频考点拆解、几道必练编程题的完整推演过程、以及从刷题到上考场的备考节奏。你完全可以拿着这篇文章当一份复习提纲来用。1. 一套研发笔试卷到底在考什么1.1 从题型分布看清考察重心研发笔试的结构这么多年变化不大大体上由客观题和主观题构成。客观题一般是选择、填空、判断分值占比大概三成到四成重点考概念理解和细节记忆。主观题则是简答加编程题简答三五道编程两三道剩下的分值大头都在这里。以这套卷子的整体风格为参考选择题常考的点集中在C对象模型、操作系统进程调度、计算机网络分层模型这类基础概念。这些概念看起来不起眼但失分率很高。不少同学复习的时候只把精力放在刷算法题上忽略了基础概念的精确表达结果选择题错一大片。编程题就算勉强做出来了总分依然不理想。这里想多说一句这类卷子的出题风格代表的是互联网公司通用研发岗位的考察思路。它先确认你大学期间有没有认真学过专业核心课再考察你解决实际问题的代码能力。简答题尤其能体现这一点题目往往长成“简述XX的原理”“说明XX与XX的区别”这种样子没有标准答案但需要你用准确、简洁的语言表达清楚。1.2 笔试为什么至今仍是筛选的第一道关卡很多同学会问既然后面有技术面试为什么还要先考一场笔试直接面试不行吗站在公司角度笔试是最低成本的大规模筛选手段。一个面试官每天能面的人数非常有限而简历又很难完全反映真实代码水平。一张笔试卷能把基础薄弱的候选人在进面之前筛掉让面试官把时间花在更值得考察的方向上比如项目经历、工程思维、沟通表达。从实际数据来看笔试阶段的淘汰率通常会达到50%到70%。这不意味着你需要考满分而是说你要超过同一批投递者的平均线。所以备考要有策略高频考点必须拿分冷门偏题可以放弃。比如死锁的四个必要条件几乎年年出现而某个特定操作系统的冷门调度算法可能两三年才出现一次两者的复习优先级差别很大。2. 高频考点逐项拆解2.1 数据结构与算法笔试永远的主角无论翻出哪一年的研发笔试卷数据结构与算法都是占分最高的模块。这套卷子里选择题至少三分之一的数量集中在链表、二叉树、排序和查找上编程题更是基本就是两道算法题。链表题看起来简单却很容易在边界条件上翻车比如反转链表时怎么保存下一个节点两个链表相交时怎么找交点。笔试考链表不是单纯想考你背代码而是因为链表的操作能直接体现你对指针或者引用的理解程度。我当时复习的时候把单链表反转、合并两个有序链表、判断链表是否有环这三道题反复练了很多遍。练到什么程度呢就是看到类似题基本不用过脑子直接条件反射写代码。笔试现场时间紧张这种熟练度能帮你节省出大量时间给后面的难题。二叉树出现的频率也相当高最常见的几种考法是非递归实现三种遍历、层序遍历、求最近公共祖先、二叉搜索树的插入与删除。最近这些年越来越流行考“层序遍历逐行输出”因为这道题能同时考察队列的使用和边界判断。动态规划与贪心同样是高频区典型题目有背包问题、最长上升子序列。这类题目考察的核心不是代码而是状态转移方程能不能推出来。这里整理一个排序算法复杂度的对照表笔试前建议至少过一遍排序算法平均时间复杂度最坏时间复杂度空间复杂度稳定性冒泡排序O(n²)O(n²)O(1)稳定快速排序O(n log n)O(n²)O(log n)不稳定归并排序O(n log n)O(n log n)O(n)稳定堆排序O(n log n)O(n log n)O(1)不稳定插入排序O(n²)O(n²)O(1)稳定有些同学只记复杂度数值忽略了稳定性这个维度。实际上凡是考排序的笔试题稳定性都是一个绕不开的考点尤其是选择题经常以“下列哪个排序算法是稳定的”这种形式出现。2.2 操作系统与计算机网络简答题的高发区操作系统和计算机网络是简答题的集中出题区域。操作系统常考的包括进程与线程的区别、死锁的必要条件、页面置换算法、银行家算法计算机网络则喜欢问TCP三次握手为什么是三次、TCP和UDP的区别、浏览器输入网址之后的完整流程。这里给一个实用建议不要背结论要会讲给别人听。简答题不是填空题只写关键词只能拿到一半分数。比如问“为什么TCP握手需要三次”如果只回答“因为要确认双方的收发能力”这个答案不够完整。完整的回答应该包括第一次握手确认了客户端的发送能力第二次握手确认了服务端的收发能力第三次握手确认了客户端的接收能力。更重要的是需要说明如果没有第三次握手会有什么问题比如历史重复的连接请求可能导致服务端资源被白白浪费。计算机网络还有一个高频考点是HTTP状态码。301和302的区别、401和403的区别、504和502的区别这些都是选择题和简答题的常客。我当时用的方法是把状态码按类别整理成表格反复默写直到形成条件反射。2.3 数据库与语言基础看似送分实际最容易丢分数据库这一部分主要考SQL编写、索引失效场景、事务的ACID特性。SQL题目通常是给两张表要求写出查询语句难度不高但细节要求很严格。一个常见的错误是写GROUP BY的时候不遵守“查询列必须包含在分组列或聚合函数中”这个规则。在MySQL的ONLY_FULL_GROUP_BY模式下这种SQL会直接报错即使不报错阅卷人也会扣分。语言基础方面C的三大特性、虚函数表、构造和析构函数的调用顺序、智能指针这些都是高频考点。如果投的是Java岗位考点就换成HashMap原理、JVM内存区域划分、线程池核心参数。考试前一定要确认自己投递的岗位使用的是哪种语言然后针对对应语言做重点复习。C的构造函数和析构函数顺序是一个很经典的坑比如基类和派生类的构造顺序是先基类后派生析构顺序则正好反过来。选择题经常把这个顺序打乱让你判断对错。如果不熟悉继承体系下的构造析构顺序这类题基本靠蒙。3. 几道必练编程题的完整拆解3.1 单链表反转的迭代与递归这类卷子出现概率最高的编程题之一就是单链表反转。代码量少却能完整考察指针操作能力。题目描述很直接给定一个单链表将它反转返回反转后的头节点。迭代解法的核心是三个指针prev、cur、next。遍历过程中先保存cur的下一个节点到next再把cur的next指向前一个节点prev然后prev移动到curcur移动到next。循环结束后prev指向的节点就是新链表的头节点。struct ListNode { int val; ListNode* next; ListNode(int x) : val(x), next(nullptr) {} }; ListNode* reverseList(ListNode* head) { ListNode* prev nullptr; ListNode* cur head; while (cur ! nullptr) { ListNode* next cur-next; cur-next prev; prev cur; cur next; } return prev; }这段代码里最容易被扣分的点有三个。第一忘记保存next节点导致cur移动后链表断掉后面的节点全部丢失。第二循环条件写错成cur-next这样当cur指向最后一个节点时循环提前退出反转结果少一个节点。第三函数最后返回了cur而不是prev此时cur已经指向nullptr返回结果自然不正确。递归版本的代码更短但理解起来反而更难。核心思想是先反转后续链表再把当前节点接到尾部ListNode* reverseListRecursive(ListNode* head) { if (head nullptr || head-next nullptr) { return head; } ListNode* newHead reverseListRecursive(head-next); head-next-next head; head-next nullptr; return newHead; }实际笔试中如果题目没有明确要求用递归我建议优先写迭代版。递归虽然看起来简洁但一旦链表很长可能会导致栈溢出。笔试阅卷人通常更看重代码的健壮性。3.2 合并两个有序链表合并两个有序链表同样属于链表基本功和反转链表一样经常出现在笔试试卷中。题目描述是给定两个升序链表合并成一个新的升序链表并返回。迭代解法推荐使用dummy节点技巧。创建一个dummy节点作为新链表的头用tail指针维护新链表的末尾。两个指针分别遍历两个链表每次取出较小的节点接在tail后面最后把剩余部分整体接上。ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) { ListNode dummy(0); ListNode* tail dummy; while (l1 ! nullptr l2 ! nullptr) { if (l1-val l2-val) { tail-next l1; l1 l1-next; } else { tail-next l2; l2 l2-next; } tail tail-next; } tail-next (l1 ! nullptr) ? l1 : l2; return dummy.next; }dummy节点这种写法在链表操作里非常实用。它避免了处理头节点为空的特殊情况让代码统一简洁。如果不用dummy节点往往需要额外判断新链表头是否为空代码会复杂不少也容易写漏。这道题的时间复杂度是O(nm)空间复杂度是O(1)。笔试答题时建议在代码开头简单写一句思路说明再贴代码最后标注复杂度。这会让阅卷人一眼就能看出你的逻辑是清晰的。3.3 经典动态规划最长上升子序列动态规划题里最长上升子序列是出现频率很高的一道。题目是这样的给定一个无序数组找到其中最长严格上升子序列的长度。dp数组的定义是dp[i]表示以nums[i]结尾的最长上升子序列长度。转移方程是对于每个i遍历所有j小于i如果nums[j]小于nums[i]则dp[i]max(dp[i], dp[j]1)。初始时每个位置的dp值都是1因为单独一个元素也算一个上升子序列。int lengthOfLIS(vectorint nums) { int n nums.size(); if (n 0) return 0; vectorint dp(n, 1); int res 1; for (int i 1; i n; i) { for (int j 0; j i; j) { if (nums[j] nums[i]) { dp[i] max(dp[i], dp[j] 1); } } res max(res, dp[i]); } return res; }这个解法的时间复杂度是O(n²)。笔试中写出这个版本已经足够拿到大部分分数。如果还有余力可以考虑用贪心加二分优化到O(n log n)但思路相对复杂不是必写项。动态规划题最怕的不是不会写代码而是定义不对dp数组。定义错了后面的状态转移全都无从谈起。笔试遇到动态规划题我的习惯是先在草稿纸上写出dp数组的含义和转移方程确认无误后再动代码。3.4 一道必练的SQL题查询选了所有课程的学生编程题之外数据库题也经常出现。一个典型场景是选课表有三张表学生表student、课程表course、选课表score。要求查询选修了所有课程的学生姓名。这道题有多种写法最稳妥的是使用双重NOT EXISTSSELECT student_name FROM student s WHERE NOT EXISTS ( SELECT 1 FROM course c WHERE NOT EXISTS ( SELECT 1 FROM score sc WHERE sc.student_id s.student_id AND sc.course_id c.course_id ) );这个查询的逻辑是不存在任何一门课程是这个学生没有选过的。双重否定等于肯定查询结果就是选了所有课程的学生。SQL题的成绩高低往往不在于语法多高级而在于能否保证在边界输入下结果正确。比如没有学生选课时这个查询返回空集这就是正确行为。我见过不少同学自作聪明地用GROUP BY加COUNT然后和课程总数比较。这种方式在课程表无重复记录时可行一旦课程表有重复或者特殊数据结果就可能出错。笔试环境下优先选择逻辑清晰、不容易出错的写法。4. 备考实战从刷题到上考场的完整策略4.1 笔试时间分配与答题顺序很多同学平时刷题没问题一到限时笔试就慌乱核心原因是没有提前规划好时间分配。以人人网2015研发笔试卷B这种量级的卷子为例总时长一般是90到120分钟。我建议的分配方法是先用10分钟扫读全卷标记出简单题和难题然后按照“选择填空、简答题、编程题、检查”的顺序作答。选择填空控制在25分钟内简答题控制在30分钟编程题至少留出30分钟最后留10分钟检查。检查阶段重点看两样东西一是选择题有没有填错题号二是编程题有没有明显的拼写错误和变量名不一致问题。笔试现场的代码往往不允许你编译调试所以检查这一步格外重要。编程题答题时先写思路再写代码。哪怕只是简单的两行注释说明你的解法思路也能帮助阅卷人理解你的代码逻辑。最忌讳的是上来就甩一大段代码没有任何解释一旦代码写得乱阅卷人很难判断你是不是真的理解这道题。4.2 最容易踩的五个坑根据我自己刷题和帮同学看卷子的经验研发笔试最常见的失分原因有这么几个第一选择题犹豫太久一题卡住超过三分钟还不跳。笔试的时间是固定的在一道题上花费过多时间后面的编程题时间必然被压缩。遇到没把握的选择题先凭第一印象选一个并标记最后有时间再回头思考。第二编程题不写注释代码可读性差。笔试代码是人工阅卷阅卷人没有时间逐行分析你的代码逻辑。适当写注释不仅能帮阅卷人理解你的思路还能在你代码有瑕疵时帮你争取到过程分。第三简答题写太短。只写结论不写分析得分往往不高。一个完整的简答应该包含结论、理由、延伸补充三部分。哪怕多写两句话解释原因得分都会不一样。第四忽略输入输出的边界情况。编程题里链表头节点为空、数组长度为零、字符串包含空格这些情况都应当在代码中处理。很多时候代码正确但一遇到空输入就崩溃这会导致大量丢分。第五时间不够时没有优先级概念。最后一分钟发现编程题写不完要优先把主体逻辑写完至少保证核心部分是正确的没有时间再去补注释。4.3 刷题之外的自我检测方法除了刷笔试卷本身我强烈建议每复习完一个模块做一次“白纸默写”。合上资料把TCP三次握手的过程画出来把HashMap的put流程写出来把快排的Partition步骤写出来。这种输出式复习比反复看资料有效得多。看资料是输入默写是输出只有能顺利输出才说明知识真正进了脑子。还有一个方法是用错题本。不是所有错题都要记只记那些“看了答案之后觉得恍然大悟”的题目。这类题往往对应一个你长期没搞懂的知识盲点把它整理清楚隔三天再重新做一遍直到完全掌握。这套卷子的价值不在于押中原题而在于帮你明确短板。做完之后如果发现操作系统选择题错了一半说明你的计算机基础需要重新补齐如果编程题耗时过长说明你的代码熟练度还不够。知道问题出在哪里后续复习才能有的放矢。4.4 刷题数量的节奏安排关于刷题数量我的经验是分三个阶段。第一阶段是熟悉期按照知识点专项刷题比如这一周只刷链表题下一周只刷树相关题。这个阶段的目标是建立知识框架不用太在意速度。第二阶段是强化期开始做整套笔试卷完全按照考试时间限制来。这个阶段的目标是训练时间分配和应试心态。第三阶段是冲刺期重点复习错题和高频知识点不再接触大量新题保持手感即可。很多人准备笔试容易陷入一个误区觉得刷的题越多越好。实际上研发笔试考察的知识点范围相对固定与其刷一千道题每道都一知半解不如把一百道经典题吃透掌握每种题型的套路和变体。算法题的核心是思路思路通了代码只是表达方式的问题。把时间和精力花在建立类型化思维上远比盲目追求刷题数量划算。这套卷子我保存了很多年指导学弟学妹准备笔试时也经常用到。它不算难但也绝不算简单正好卡在“认真复习就能过裸考大概率挂”的位置上。如果你正在准备研发岗笔试不妨拿它当第一套自测卷。做题的时候发现某一类题完全没思路不用慌那恰恰说明你找到了接下来需要重点复习的方向。祝大家笔试顺利早日拿到心仪的offer。
返回列表