十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

网易有道研发工程师笔试题解析:从数据结构到算法实战

网易有道研发工程师笔试题解析:从数据结构到算法实战 那年秋招我前后投了十来家公司的研发岗网易是其中准备得最久的一家。原因倒也不复杂有道事业部做的词典、翻译、云笔记这些产品都属于典型的“工具类吃技术细节”的业务既有用户量又有算法挑战和我想做的方向很匹配。收到2018校招研发工程师有道事业部笔试卷的时候心里其实有点底但真打开题目还是被一套东西“上了一课”——选择和填空占了将近一半分值编程题反而不像想象中那么简单。这套题给我的整体感受是网易的笔试并不追求偏题怪题而是在基础知识的深度和边界条件上做文章谁的基本功扎实谁就能在有限时间里面稳定拿分。这篇文章我就以这套有道事业部研发工程师笔试卷为线索把整张卷子的题型分布、核心考点、编程题的完整解题思路以及我在笔试现场犯过的错误和复查方法全部拆开讲一遍。无论你是正在准备校招的应届生还是自学基础想进大厂的技术人相信都能从这套题里收获一些判断自身水平的方法。1. 一套笔试卷的构成逻辑与备考重心1.1 题型分布与分值权重网易2018校招研发工程师有道事业部的笔试卷整体结构是“选择填空 简答/设计 编程题”三件套。我印象里在牛客网上做的时候答题时间120分钟满分100分其中单选、多选这类客观题加起来大概40分上下简答题10到15分编程题两道到三道占40到50分。这个分值配比本身就在传递一个信号它不希望招到只会背题的人而是希望候选人能够在压力环境下同时兼顾正确率和速度。客观题覆盖的范围很广几乎把计算机基础全部扫了一遍数据结构、算法、操作系统、计算机网络、数据库外加一门到两门语言基础Java和C/C是重点部分岗位会涉及Python。这类题的难点不在于单个知识点有多深而在于它考得密。你可能刚写完一道链表的题下一道就跳到了TCP四次挥手的TIME_WAIT状态再下一道又变成了B树索引在磁盘I/O上的优势。大脑需要在不同模块之间快速切换这种能力本身也是笔试要考察的。简答题往往是围绕业务场景设计的比如“如何设计一个高可用的词典查询接口”或者“给出一个英文分词模块的设计思路”。有道事业部属于工具产品线它的技术团队非常看重候选人对真实业务场景的理解能力而不是单纯的理论背诵。编程题则会更直接地考察算法能力有一道经典的动态规划也有一道基于拓扑排序的图论题难度大约在LeetCode中等偏上但坑点设置比LeetCode更细腻边界条件很容易踩。1.2 有道事业部的选人侧重点网易有道的产品矩阵以翻译、词典、云笔记、在线教育为主这套产品的共同特点是“数据规模大、实时性要求高、用户操作路径短”。词典的一次查词要经历查缓存、分词、词典匹配、例句检索、发音资源复用等多个环节任何一环响应变慢用户都会立刻感受到。因此笔试环节对有道研发工程师的要求也会明显偏向工程实现能力。具体到题目设计上你会看到很多题目背后藏着产品的影子。字符串处理相关的题目明显偏多因为有道词典的核心场景就是文本处理缓存和并发控制的题目也有出现因为云笔记的同步服务、翻译的并发请求都属于典型的高并发场景甚至有一道选择题直接拿“网易云音乐”的播放列表做背景问数据结构和存储选择这说明网易在出题时并不避讳拿自家产品当案例反而是希望你通过题目去理解它们背后的技术决策。备考的时候如果只看纯算法书不结合产品思维去思考遇到这类题会有点吃亏。我给后来人的建议是复习基础题的同时把网易系的产品有道词典、有道翻译、网易云音乐、网易云课堂的使用流程在心里过一遍想清楚每一步背后可能涉及的数据结构和系统设计。不是为了押题而是为了培养“技术服务于产品”的直觉。2. 选择题里的高频考点与易错陷阱2.1 数据结构与算法二叉树、哈希、排序必考这套笔试卷的选择题里数据结构部分至少有三分之一的分值。二叉树几乎是雷打不动的考点我记得有一道题是给出一棵二叉树的前序遍历序列和中序遍历序列要求还原出后序遍历序列。这种题本身不难但很多人会在“还原树”的过程中因为粗心丢掉根节点顺序。其实解法是有固定套路的前序遍历的第一个元素一定是根拿着这个根去中序遍历里切分左右子树再递归处理。我当时为了省时间直接用了栈模拟递归把序列关系画出来之后一目了然。哈希冲突的处理方式也是高频考点。网易的题喜欢把“链地址法”和“开放定址法”放在一起对比再追问“在元素个数已知、内存有限的情况下哪种方式更容易产生聚集效应”。这里的关键是理解开放定址法中的线性探测会造成主聚集二次探测可以缓解但仍有二次聚集而链地址法本质上是用指针换空间不存在这类问题。还有一道多选题考察了Java 8中HashMap在链表长度超过8时转为红黑树的细节这个点如果没读过源码很容易判断错。排序算法的稳定性和时间复杂度几乎是每张技术笔试卷的常客。网易出题比较细不是简单问你快排时间复杂度是多少而是给你一个“近似有序”的数组让你选最优排序算法。插入排序在这种场景下能做到接近O(n)的复杂度而快排因为递归栈和分区不均匀反而可能退化。还有堆排序的不稳定性很多人会误以为堆排序是稳定的实际上它在调整堆的过程中会破坏相同元素的前后顺序。这类细微差异就是笔试拉开差距的地方。2.2 计算机网络与操作系统TCP、进程与内存管理网络部分的首选考点肯定是TCP。网易的题喜欢把三次握手和四次挥手混在一道多选题里考让你判断哪些说法正确。我印象最深的是关于TIME_WAIT状态的讨论主动关闭连接的一方为什么要进入TIME_WAIT并且等待2MSL答案是两个一是为了保证最后一次ACK能到达对端如果丢失可以重发二是为了让旧连接的所有报文段在网络中消失避免干扰新连接。这两个原因都有对应的场景理解比死记硬背重要得多。操作系统部分进程与线程的区别是必问的。但网易的题多了一个角度它拿了网易云音乐PC端的场景举例问一个音乐播放器进程在播放音频的同时要渲染歌词、响应列表点击、下载缓存文件用多线程还是多进程实现更合理这道题的考点本质上是共享内存和上下文切换的开销。多线程共享地址空间切换代价小适合需要频繁共享数据的场景多进程隔离性强但通信成本高。播放器这类强交互应用主流实现都是多线程加锁或线程池而不是多进程。内存管理里有个经典概念叫虚拟内存和页表网易考的并不难但很绕有一段代码频繁访问一个比物理内存大得多的数组问系统表现如何。答案是会产生大量缺页中断导致频繁的页面置换性能急剧下降这个现象叫“抖动”。如果你不理解页表命中率和局部性原理很容易被其他选项干扰。我当时的技巧是把整个流程想成一个“快递柜”内存是柜子程序要用的数据是快递柜子不够用时只能频繁取换时间全花在开门关门上了。2.3 数据库与语言基础索引、SQL与Java基础数据库部分网易把重点放在索引和SQL优化上。有道词典的词条数据量大查询频繁所以索引是它们技术笔试几乎必出的内容。选择题常见问法是在什么情况下即使字段上有索引查询也不会走索引答案包括使用函数运算、隐式类型转换、LIKE通配符在最前面、对索引列进行表达式计算。有一道题我记得特别清楚给了四个SQL语句让选出能用上联合索引 (a, b, c) 的查询组合这题真正考的是“最左前缀原则”不是背会索引结构就够的。语言基础方面Java和C/C都有涉及。Java的必考点是String的不可变性和字符串常量池有一道题问“String s1 new String(abc)和String s2 abc的区别”答案是前者创建了两个对象堆中对象和常量池中的字面量如果常量池没有的话后者最多创建了一个。如果面试岗位偏服务端还会考HashMap在多线程环境下的问题以及volatile和synchronized的区别。C方向则更爱考虚函数表、指针引用区别、构造函数和析构函数的执行顺序。我当时学的是Java为主C基本靠突击吃了一个亏建议投递前先看清楚岗位要求再定复习主次。3. 编程题从读题到AC的关键几步3.1 有序数组区间去重双指针的标准解法这张笔试卷的第一道编程题是一道“有序数组去重”的变体。题目大意是给定一个已按升序排列的整数数组要求原地删除重复出现的元素使得每个元素最多出现一次并返回新数组的长度。不能使用额外数组空间必须在原数组上操作空间复杂度要求O(1)。这道题本质上就是LeetCode 26题的翻版但它多了一个容易忽略的细节数组允许同时包含负数而且长度可能为0。我看到题目后第一反应是直接遍历并创建新数组但马上被空间限制否决。正确的做法是双指针一个指针slow指向当前不重复区域的尾部一个指针fast遍历整个数组。当fast指向的值不等于slow指向的值时将fast的值复制到slow的下一个位置然后两个指针都前进如果相等只移动fast。public int removeDuplicates(int[] nums) { if (nums null || nums.length 0) { return 0; } int slow 0; for (int fast 1; fast nums.length; fast) { if (nums[fast] ! nums[slow]) { slow; nums[slow] nums[fast]; } } return slow 1; }注意return的是slow1而不是slow这是很多人在最后一刻写错的地方。还有一个边界条件数组长度只有1时slow初始值是0for循环直接不执行返回1这刚好是正确结果。这个解法的时间复杂度O(n)空间复杂度O(1)是唯一符合题意的方案。在笔试现场我栽过在“是否判断null”上后来养成了写代码前先把空数组、单元素数组、全重复数组三种情况在注释里列出来的习惯。3.2 最长公共子序列动态规划的经典套路第二道编程题是“求两个字符串的最长公共子序列LCS的长度”。有道的产品里句子翻译、例句匹配、查词历史相似度比对都会用到类似算法所以这道题对它们来说非常对味。输入是两个只含小写字母的字符串长度分别不超过1000要求输出最长公共子序列的长度。动态规划的核心是状态定义用dp[i][j]表示字符串A的前i个字符和字符串B的前j个字符的最长公共子序列长度。状态转移分为两种情况如果A[i-1]等于B[j-1]那么dp[i][j]dp[i-1][j-1]1如果不等则取dp[i-1][j]和dp[i][j-1]的较大值。这里有个易错点为什么不等的时候是取这两个值而不是别的因为A的第i个字符和B的第j个字符既然不匹配那当前的最长公共子序列要么来自“A前i-1个字符和B前j个字符”的结果要么来自“A前i个字符和B前j-1个字符”的结果两者取最大即可覆盖所有情况。def lcs(a: str, b: str) - int: m, n len(a), len(b) dp [[0] * (n 1) for _ in range(m 1)] for i in range(1, m 1): for j in range(1, n 1): if a[i - 1] b[j - 1]: dp[i][j] dp[i - 1][j - 1] 1 else: dp[i][j] max(dp[i - 1][j], dp[i][j - 1]) return dp[m][n]这道题我平时刷过很多遍但考试时还是花了将近20分钟原因是在边界索引上反复确认dp数组的大小是(m1)×(n1)防止dp[i-1]在i0时越界。内存方面m和n都是1000所以二维数组是100万级别的int约4MB完全可以接受。但如果题目把长度扩到10万就需要用滚动数组压缩到两行这是进阶写法。网易给的约束刚好卡在二维DP能承受的范围说明出题人也是希望考生先掌握标准做法。3.3 课程表问题拓扑排序与环检测第三道编程题是一个典型的“课程表”问题和LeetCode 207几乎一样。题目描述得很生活化你一共需要修n门课程编号从0到n-1有些课程需要先修课程比如你想修课程1必须先修课程0这种关系用一个二维数组prerequisites给出其中prerequisites[i] [a, b]表示学习课程a之前必须先学习课程b。问是否可能完成所有课程。这个问题的本质是判断一个有向图是否存在拓扑排序或者说判断图中是否有环。核心思路是用入度表加广度优先搜索先统计每个节点的入度把入度为0的节点放入队列依次出队并把它指向的节点的入度减1如果某个节点的入度减到0就加入队列。最后如果出队的节点数等于总课程数说明可以完成否则说明图中存在环无法完成。public boolean canFinish(int numCourses, int[][] prerequisites) { ListListInteger adj new ArrayList(); int[] indegree new int[numCourses]; for (int i 0; i numCourses; i) { adj.add(new ArrayList()); } for (int[] pre : prerequisites) { adj.get(pre[1]).add(pre[0]); indegree[pre[0]]; } QueueInteger queue new LinkedList(); for (int i 0; i numCourses; i) { if (indegree[i] 0) { queue.offer(i); } } int count 0; while (!queue.isEmpty()) { int cur queue.poll(); count; for (int next : adj.get(cur)) { indegree[next]--; if (indegree[next] 0) { queue.offer(next); } } } return count numCourses; }这道题考察的细节有两个一是图用邻接表存储而不是邻接矩阵因为prerequisites可能很大邻接矩阵会浪费空间二是有可能某个课程根本不在依赖关系中它的入度天然就是0一开始就会被加入队列这一部分不要漏掉。我在考场上的失误是忘记了count计数器差点直接按队列是否为空来判断那样的话环里的节点没入过队队列确实会空但结果却是错的。加上count之后逻辑才闭环。4. 开放设计题如何让阅卷老师眼前一亮4.1 设计一个高可用的词典查词接口简答题里有一道让我印象很深“有道词典App中用户输入一个单词并点击查询请设计后端查询服务的整体流程要求满足高并发和低延迟。”这种题没有标准答案但判卷人大概会按照你的逻辑链是否完整、是否考虑异常场景来给分。我的回答分成了四层。第一层是缓存策略查词请求先查本地缓存Redis如果命中直接返回不命中再走后续逻辑。缓存key用单词本身value用JSON序列化的词条信息TTL设为24小时左右。第二层是索引结构词典数据量很大不可能每次查询都扫全表所以用倒排索引或者B树索引来加速精确匹配。第三层是服务降级如果数据库压力过大可以用消息队列削峰或者开启限流保证核心查词链路不被打挂。第四层是容灾查询服务要做多机房部署某个机房挂了流量要能自动切到另一个机房。这套设计其实谈不上多高大上但胜在每一步都有明确理由。阅卷老师也是从业者他们最反感的是堆砌技术名词却不解释原因。我后来复盘时觉得如果能在答案里加上一句“缓存层需要注意缓存穿透问题比如用户连续查询不存在的单词所有请求都会直接打到底层存储”得分会更高。因为这些细节证明你不仅知道缓存还知道缓存在实际业务里会怎么失效。4.2 结合音乐场景的算法问题从笔试题到产品思维还有一道简答题把场景放在了网易云音乐上要求“简述如何根据用户的历史播放行为推荐风格相似的歌曲”。这道题表面上是推荐系统的问题但结合标题中的“研发工程师”岗位它更想考察的是你对工程落地的理解。我的思路是先讲数据从哪来用户播放日志是一个持续产生的数据流需要经过清洗、去重、会话切分才能得到“用户在某个时间段听了哪些歌”的有效序列。然后是相似度计算歌曲可以用风格标签、音频特征、歌曲ID的共现矩阵来表示。工程上最常用的手段是协同过滤就是“喜欢歌A的用户也喜欢歌B”这个规律用共现矩阵来挖掘。最后是实时与离线结合离线离线算好相似歌单并缓存线上根据用户当前播放的歌曲实时拉取相似歌曲列表做一个简单的混合排序。这道题不难但需要你站在产品的角度想“推荐结果为什么是这样”而不是机械地背诵“协同过滤分为基于用户和基于物品两种”。我建议所有准备综合面试的人平时都试着把技术名词翻译成业务语言比如“协同过滤”就是“让相似的人互相种草”“TF-IDF”就是“找出一篇文章里哪些词最特别”。这种翻译能力在笔试简答和面试中都极其吃香。5. 笔试现场的坑与复盘心得5.1 时间分配遇到卡壳先跳过我自己在做这套笔试卷时最痛的教训发生在选择题上。当时有一道关于数据库隔离级别的多选我纠结了将近8分钟反复回忆“可重复读”“读已提交”“幻读”之间的关系结果越纠结越不确定。最后只能随便选了一个成功地把后面编程题的时间压缩了。赛后复盘这道题其实只值2分而编程题一道就是20分起步这个账怎么算都不划算。正确的策略是遇到拿不准的客观题先标记出来按第一感觉选一个答案然后立刻跳过去。编程题优先做自己最有把握的比如那道有序数组去重我大概10分钟内就能写完并测试拿分确定性最高。第二道LCS需要20到25分钟。第三道拓扑排序如果顺利15分钟也能搞定。这样即使某道选择题全部猜错总分也能保住80分以上。笔试的时间管理本质上是一个投资问题单位时间内的期望得分才是决策依据而不是题目在试卷上的顺序。我见过很多同学被一道多选卡住导致后面三道编程题只写了一半这种情况最可惜。5.2 边界条件代码能不能AC的关键编程题的WAwrong answer80%以上不是因为算法思路错而是边界条件没处理好。网易这几道题我踩过的坑基本可以整理成一张表这里写出来供大家直接抄作业。问题类型典型场景处理方式空输入数组长度为0、字符串为空在函数开头显式判断并返回对应值单元素输入数组只有1个元素、n1确认循环边界和返回值的下标关系重复元素集中在开头/结尾全相同数组、全相异数组用最小和最大规模的用例各跑一遍索引越界二维DP、邻接表遍历给dp数组加一行一列作为哨兵溢出的负数数组包含负数和0比较时不要假设元素都是正数我写代码前会在注释里把边界用例列出来比如“数组长度为0时输出0”和“数组只有一个元素时输出1”先在脑内运行一遍再落笔写循环。这样做的好处是循环里的索引设计会自然避开越界问题。实际考试中这个习惯帮我保住了一半以上的隐藏测试用例。5.3 复盘从笔试到面试的衔接笔试结束不等于求职结束。我做完整张卷子后第一时间做的事是记录每道题背后的知识点用手机备忘录列出了大概15条双指针去重、DP状态转移、拓扑排序入度表、TIME_WAIT原因、最左前缀原则……一个星期之后我拿到面试通知时就拿着这份清单去准备。面试官问的技术问题几乎都能在笔试题清单里找到影子。这是大厂笔试的一个潜规则笔试卷子不只是筛选工具它也是一份内部出的“考点大纲”面试官出题时多多少少会顺着笔试的重点走。所以你花在复盘笔试上的时间全都不是浪费它会直接转化为面试环节的命中率。我当时把每道编程题都重新写了一遍并且试着用不同的解法实现比如LCS改用滚动数组、拓扑排序改用DFS面试时被问到“有没有更好的空间优化方案”我直接拿出滚动数组的解法讲了3分钟明显感觉到面试官态度有了变化。再分享一个小技巧笔试时做过的代码和建议哪怕是草稿也要在结束后马上誊写一遍到自己的代码仓库里。不需要整理得多漂亮只要保证“过了三天还能看懂自己当时为什么这么写”。我后来在面试里遇到过一道极为相似的题目直接打开代码仓库边看边讲效果比临时在面试官面前推演好得多。这套流程我后来一直沿用到所有公司的笔试复盘里投入产出比非常高。
返回列表