ARTICLE DETAIL

资讯详情

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

网易有道校招算法笔试题解析:KMP、动态规划与机器学习考点复盘

网易有道校招算法笔试题解析:KMP、动态规划与机器学习考点复盘 网易2018校园招聘算法工程师有道笔试卷这是一套很有代表性的校招真题。那会儿我还在准备秋招刷到这套卷子的时候第一感觉是相比纯互联网大厂动辄四道编程题的卷子网易有道的算法笔试题更“综合”除了写代码还会认真考察概率统计、机器学习基础和业务思维。如今回看虽然年份稍早但其考点分布和命题思路依然值得准备算法岗的同学考古参考。特别是KMP的next数组推导、动态规划、贪心、图论和机器学习基础这些内容在校招笔试里出现的频率一直很高。这套卷子的内容目前网上能搜到的基本是当年考生在牛客网、知乎、博客里的回忆版并非官方完整原卷。但正因为是回忆整理反而更接近实际考场上大家能感知到的重点。这篇文章我按“试卷结构 → 核心考点拆解 → 编程题实操复盘 → 机器学习与概率统计 → 备考建议”来写尽量把每一类题背后的知识点和解题套路讲透。1. 试卷整体结构网易有道的算法笔试到底考什么1.1 从岗位倒推考点网易有道2018年时的主要产品线包括有道词典、有道翻译、有道云笔记、有道精品课等这些产品都有大量搜索、推荐、NLP相关的业务场景。所以它的算法工程师笔试不是纯粹的LeetCode刷题比赛而是带着业务味道的选拔既要求你数据结构与算法基本功扎实又要求你懂机器学习基础还希望你有一定的概率统计功底。这一点可以从题型分布上看出来。一个典型的算法岗笔试试卷大致会有单选题、多选题、编程题以及问答/简答题。单选题里会出现“以下哪种排序算法平均时间复杂度最低”这类送分题也会出现“一个袋子里有红球白球取两次不放回求第二次取到红球的概率”这类概率题。编程题一般有两到三道难度梯度拉开第一道往往是字符串或者模拟题后面会出现DP或者搜索题。我当时拿到这套卷子第一反应是先把所有题目浏览一遍标出会做的和不会做的优先把送分题拿下再啃硬骨头。这个策略在时间紧张的笔试中非常关键。1.2 典型题型分布按照常见校招算法笔试的口径我把这类卷子的模块和占比整理成一张表方便你对照复习考察模块常见考点预估占比数据结构与算法栈、队列、链表、二叉树、哈希、排序、KMP、二分、贪心、DP、图论35% - 45%概率统计与数学古典概型、条件概率、期望、随机变量、排列组合10% - 15%机器学习基础过拟合、正则、交叉验证、特征选择、常见模型对比10% - 15%深度学习与NLP词向量、RNN/LSTM、注意力机制、文本分类5% - 10%编程题字符串处理、动态规划、搜索、模拟、手写数据结构30% - 40%当然每年每套卷子的权重会有浮动但这张表基本能反映网易有道的命题倾向。相比腾讯笔试喜欢出大量计算机基础比如网络和操作系统网易有道的算法岗试卷明显更聚焦在“算法与数据科学”相关的内容上。1.3 命题风格里藏着的业务影子做这套卷子你会发现题目有时候会披着一层业务外衣。比如“用户在搜索引擎输入一个词返回一系列结果如何评估排序质量”、“给定一批用户行为日志如何设计特征预测点击率”之类的描述。这类题目表面考机器学习实际是看你能不能把算法落地到具体场景里。有道的算法工程师很大一部分工作涉及词典数据挖掘、翻译质量评估、搜索排序和推荐。笔试环节出现业务化描述是为了提前筛选出那些“只会调包但不懂业务逻辑”的候选人。所以备考时不要只看算法题还要想想这些算法用在哪、为什么用、有什么坑。2. 核心考点拆解数据结构和算法部分2.1 常考数据结构栈、队列、二叉树与哈希表数据结构部分选择题和编程题都会涉及到。栈常考的是括号匹配、表达式求值队列常考的是BFS和循环队列二叉树是重头戏遍历方式、层级遍历、二叉搜索树性质、最近公共祖先都是高频考点哈希表则更多是结合工程设计来考比如哈希冲突的解决方式、负载因子、扩容策略。我建议你把二叉树的非递归遍历写法背到条件反射的程度。笔试环境下递归容易栈溢出而且有些题目明确要求迭代实现。非递归前序、中序、后序、层序遍历各写一遍其实也就几十行代码但考场上省下来的时间很宝贵。哈希表部分要理解链地址法和开放定址法的区别以及为什么Java的HashMap在链表过长时会转成红黑树。这些内容看起来基础单选多选都能出而且容易被忽视。2.2 排序算法不只是背复杂度排序算法几乎必考但很少直接让你写一个快速排序。更多考察的是复杂度分析、稳定性、适用场景以及排序算法思想在其它题目中的应用。比如快速排序最坏时间复杂度是O(n^2)堆排序是O(n log n)且不稳定归并排序稳定但需要额外空间。这些知识点本身不难但要在选择题里快速判断就需要你真的理解每一层递归发生了什么。像“在完全乱序的大数据量场景下哪种排序最快”“STL的sort底层用了什么混合策略”这类问题考察的就是对排序工程实现的了解。另外堆排序的思想经常用于TopK问题归并排序思想用于外部排序。如果你在编程题里遇到“从海量数据中找最大的K个数”能想到用小顶堆而不是全排序这就体现出了工程思维。2.3 KMP算法与next数组推导字符串匹配是校招笔试里的常客KMP更是热搜词里的高频内容。网易有道的试卷里出现KMP相关的题我并不意外。这类题不会让你从零发明KMP而是考察你是否理解next数组的推导以及模式串失配时到底怎么跳。以模式串 p abacaba 为例常见定义是 next[i] 表示 p[0:i1] 这个子串的“最长相等真前后缀长度”不包含子串自身。我们逐步推导i 0子串是 a没有真前后缀next[0] 0。i 1子串是 ab前缀 a后缀 b不等next[1] 0。i 2子串是 aba前缀 a 等于后缀 a最长长度为1next[2] 1。i 3子串是 abac前缀 a、后缀 c不等前缀 ab、后缀 ac不等next[3] 0。i 4子串是 abaca前缀 a 等于后缀 anext[4] 1。i 5子串是 abacab前缀 ab 等于后缀 ab长度为2next[5] 2。i 6子串是 abacaba前缀 aba 等于后缀 aba长度为3next[6] 3。所以 next 数组为 [0, 0, 1, 0, 1, 2, 3]。但注意很多教材和网上的模板会把 next[0] 定义为 -1next[i] 表示前 i 个字符长度为 i的最长相等真前后缀长度。比如《算法导论》的 π 数组和国内教材里的 next 数组定义有差异。如果按 -1 偏移的那套定义上面这个模式串可以写成 [-1, 0, 0, 1, 0, 1, 2, 3]。在考场上我强烈建议先看清楚题目给的 next[i] 到底是哪种定义再开始填数不然容易整道题崩掉。下面给出一段C的KMP next数组求解代码写代码时建议使用“前缀函数”的定义逻辑清晰且不容易出错vectorint buildNext(const string p) { int m p.size(); vectorint next(m, 0); for (int i 1, j 0; i m; i) { while (j 0 p[i] ! p[j]) { j next[j - 1]; } if (p[i] p[j]) { j; } next[i] j; } return next; }KMP的匹配过程其实就是“主串指针不回溯模式串指针按next跳转”。理解了next数组的递推逻辑做匹配部分的代码就是顺手的事。这道题如果出在选择题一般会给一个模式串和一段主串问你匹配过程中比较了几次本质上还是考察next数组理解得透不透。2.4 图论与搜索Dijkstra、拓扑排序图论在算法岗笔试里不会像ACM那样考得很难但基础算法必须会。Dijkstra是单源最短路里的明星算法哪怕不让你完整手写也会考“优先队列优化后的时间复杂度”“负权边能不能用Dijkstra”这类概念题。你要能说清楚为什么Dijkstra不能处理负权边以及Bellman-Ford和SPFA分别适合什么场景。拓扑排序也值得重视。给定一个有向无环图输出拓扑序列。这个知识点可以结合“课程安排是否存在循环依赖”这种业务化问题来考。Kahn算法是直观做法统计每个节点的入度把入度为0的节点放入队列依次处理并减少后继节点的入度。这个算法在判断有向图是否有环时也很有用。我当时复习图论时有个习惯把所有经典算法的适用条件和复杂度写在一张卡片上比如Dijkstra是贪心思想要求非负权BFS能求无权图的最短路拓扑排序只适用于有向无环图。这样面对选择题时可以快速排除错误选项。3. 编程题实操复盘思路、代码与踩坑3.1 动态规划从状态定义到边界条件网易系的编程题动态规划是常客。常见题型包括编辑距离、最长公共子序列、最长上升子序列、背包问题、股票买卖问题等。这些题在LeetCode上都有原型但笔试里往往会在输入输出上做些包装考察你能不能把实际问题抽象成状态转移。以最长上升子序列为例最直观的O(n^2)做法是定义dp[i]为以第i个元素结尾的最长上升子序列长度转移方程是 dp[i] max(dp[j] 1)其中 j i 且 nums[j] nums[i]。如果数据范围到10^5就需要借助贪心加二分的O(n log n)解法用tail数组维护上升子序列的最小末尾。我在复盘这类题时总结了一个步骤先定义状态再写转移方程然后确认初始化和遍历顺序最后考虑能否优化空间。笔试时间紧如果一开始不知道用DP可以先试试暴力递归画出递归树后再看有没有重叠子问题。这个方法在紧张状态下很管用。3.2 贪心算法什么时候敢用贪心贪心算法在笔试里考得比较多的是区间类问题。比如“给定一系列会议的开始和结束时间最多能安排多少场不冲突的会议”这个经典题按结束时间排序然后依次选择就是正确答案。但贪心最怕的是“感觉对但实际上是错的”。我建议在写贪心解法前先用小数据在草稿纸上模拟一遍至少排除明显的反例。比如经典的“硬币找零最少硬币数”问题在硬币面额为1、5、11时贪心选最大面额不一定得到最优解这种反例要能举出来。校招笔试一般不会出太偏的贪心但考察方式往往是把贪心和排序结合起来让你先排序再扫描一遍所以排序比较器的写法要熟练。3.3 手写代码的常见坑边界、溢出与输入输出编程题最容易翻车的不是算法本身而是边界条件和输入解析。当年我用C做笔试经常在快排的边界、二分查找的循环条件、字符串分割上浪费大量时间。后来我把这些常见坑整理成了检查清单空数组、链表只有一个节点时程序是否正常二分查找的左闭右开还是左闭右闭循环条件是否与mid更新一致整数加减乘除是否可能溢出尤其是求中位数用 (left right) / 2 时left right 可能超出int范围。字符串输入是否可能包含空格如果包含用cin、scanf还是getline要想清楚。输出格式是否要求保留小数点后几位题目没说就不要画蛇添足。笔试环境通常不允许调试太久所以这些边界问题必须在写代码时就有意识规避。我的习惯是写完代码后手动构造三组测试正常输入、极端输入最大值/最小值、空输入。3.4 一道完整示例旋转数组中的二分查找网易的编程题出现过类似“在一个有序数组经过旋转后查找目标值”的题目。这道题能综合考察二分查找的边界意识非常典型。我给出一个完整的C实现int search(vectorint nums, int target) { int left 0, right nums.size() - 1; while (left right) { int mid left (right - left) / 2; if (nums[mid] target) { return mid; } if (nums[left] nums[mid]) { // 左半部分有序 if (nums[left] target target nums[mid]) { right mid - 1; } else { left mid 1; } } else { // 右半部分有序 if (nums[mid] target target nums[right]) { left mid 1; } else { right mid - 1; } } } return -1; }注意 mid left (right - left) / 2 而不是 (left right) / 2就是为了防止整数溢出。很多题解不会强调这个细节但笔试和面试里这属于加分项。另外这道题还有一个变体如果数组中存在重复元素nums[left] nums[mid] 这个判断就可能失效因为可能出现“左指针、中指针、右指针三者相等”的情况此时只能暴力缩小范围。这个点如果在选择题里出现值得你展开想一想。3.5 考场上编程题的答题顺序遇到两三道编程题我的策略永远是一道一道做但会先花30秒判断每道题的难度。简单字符串题一般10分钟内搞定中等DP或搜索题控制20到25分钟如果某题想了10分钟还没有思路先跳过把后面能拿的分拿稳再回头啃。网易的笔试系统通常允许在本地IDE里写然后粘贴到网页代码框。建议先在本地跑通样例再提交到在线判题系统。毕竟网页上的编译报错信息往往比较简略本地能更快速定位问题。4. 机器学习与概率统计的考察重点4.1 机器学习基础过拟合、正则化与模型对比算法工程师的笔试不会只考代码机器学习基础是拉开差距的关键。常见考点集中在过拟合、正则化、交叉验证、特征选择、逻辑回归和SVM等经典模型。过拟合这块你要能说出“训练误差低但测试误差高”的现象以及对应的缓解手段增加训练数据、降低模型复杂度、正则化、Dropout、早停、数据增强。正则化里L1要比L2更容易产生稀疏权重这一点理解L1的梯度在0附近的不连续性就能明白。逻辑回归与SVM的对比也是高频。逻辑回归输出的是概率天然适合做排序和CTR预估SVM适合小样本高维分类核技巧能处理非线性问题。但SVM不直接输出概率需要做Platt缩放。这些对比在单选多选里经常出现所以复习时最好自己整理一张“经典模型速查表”。4.2 概率统计古典概型与期望计算概率题是网易笔试卷里稳定出现的部分。常见的题型有袋子里有3个红球5个白球不放回取两次求第二次取到红球的概率。一枚不均匀硬币正面朝上的概率为p连续抛n次求恰好出现k次正面的概率。随机变量X服从某个分布求期望和方差。贝叶斯公式的条件概率问题。这类题的核心是不要凭直觉而是把事件空间写清楚。比如“第二次取到红球”这个问题全概率公式可以拆成“第一次取红球第二次取红球”加“第一次取白球第二次取红球”两部分答案自然算出来。期望计算上线性性质常常能简化问题比如“n个独立事件出现次数的期望等于各事件期望之和”这个性质能解决很多看起来复杂的题目。4.3 深度学习与NLP基础有道做词典和翻译所以深度学习与NLP的相关知识在笔试里出现的可能性不低。2018年时Transformer刚提出不久校招考察不会太深但词向量、RNN/LSTM、注意力机制、文本分类这些概念要懂。词向量要理解one-hot的缺点和word2vec的基本思想LSTM要知道它通过门控机制缓解RNN的梯度消失问题注意力机制最简单理解是“在解码时动态地关注输入的不同部分”。如果题目给出一个小场景比如“用深度学习做情感分类文本长度不一怎么办”那你需要想到padding、截断、或者用LSTM处理变长序列。我当时备考NLP的一个取巧方法不追求手推公式而是把每个模型“解决什么问题、核心思想是什么、有什么局限性”三句话说清楚。笔试选择题考察的是理解不是推公式。4.4 遇到不会的题怎么办考试总有不会的题。我的原则是选择题不会先排除明显错误选项再用常识猜多选题拿不准的选项宁可不选因为少选还能得部分分错选直接0分编程题不会完整做也尽量写出暴力解法或部分通过很多在线判题系统是按测试点给分的。曾有一个考过网易笔试的同学告诉我他一道DP题没想出来但写出了能过前30%测试点的暴力版本最后笔试依然通过了。暴力解不是耻辱在有限时间里拿分才是硬道理。5. 常见问题与备考复盘5.1 校招算法笔试复习看什么书如果你现在离笔试还有三个月建议按这个顺序复习先过一遍《剑指Offer》的经典面试题培养常见算法题的解题手感。然后用LeetCode或牛客网刷题重点刷数组、字符串、链表、二叉树、动态规划、二分查找、贪心这几类。不需要刷难题中等题熟练就够了。算法基础薄弱的可以把《算法第4版》或者在线的“代码随想录”刷一遍配合图解理解数据结构。最后留一到两周做历年真题按笔试环境模拟练习时间分配。5.2 容易忽略但必须会的知识点不少人刷题只刷热门题结果笔试选择题里一些“冷门”知识点反而扣分。我整理了几个容易被忽略、但出现频率不低的知识点位运算异或的性质、用位运算判断奇偶、n (n-1) 去掉最低位1。二分查找的变体查找第一个大于等于target的位置、最后一个小于等于target的位置。大数据量处理海量数据去重、TopK、外部排序要知道位图和布隆过滤器的思想。手写常见数据结构栈实现队列、队列实现栈、LRU缓存。5.3 复盘一套真题的正确姿势很多同学刷真题做完对答案就扔这是最亏的。我建议每套卷子做三遍第一遍限时模拟按真实考试节奏做做完只看分数不细看答案。第二遍逐题分析把每道题背后的考点写出来标记不会的题目找到对应知识点重新学习和刷相同类型题。第三遍隔一周后重做尤其关注错题和蒙对的题确认自己真的掌握了。如果你把一套网易道笔试试卷按这个流程走下来收获会超过盲目刷十道新题。毕竟校招笔试考来考去就是这些基本功反复锤炼才是王道。5.4 简历里的项目也要经得起追问笔试通过后还有面试而面试官很喜欢从你简历里写到的算法往下问。比如你写了“用粒子群算法优化参数”那就要能回答粒子群算法和遗传算法的区别、惯性权重怎么设置、为什么收敛快但容易早熟。你写了“用BM25做搜索排序”就要理解BM25的词频、逆文档频率和文档长度归一化是怎么结合的。所以备考笔试时顺便把简历里的算法过一遍概念既能帮助笔试又能衔接面试。毕竟算法工程师这个岗位笔试只是敲门砖真正决定offer的是对算法本质的理解和落地能力。
返回列表