ARTICLE DETAIL

资讯详情

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

网易研发工程师笔试题复盘:算法、操作系统与语言底层考点解析

网易研发工程师笔试题复盘:算法、操作系统与语言底层考点解析 2016年我在图书馆刷完网易研发工程师笔试题二那个晚上印象最深的反而不是哪道题不会做而是部分题目“明明知识点都见过考场上一紧张就判断错了”。现在回头看这套题的价值在于它把研发岗核心能力拆成了几个固定维度编码基本功、操作系统与网络、语言底层、概率建模。哪怕今天再面试这些底子依然适用。这篇复盘我不打算按套题顺序逐题讲而是挑几类有代表性的题目把当时的解题思路、容易踩的坑、以及背后真正想考察的能力拆开说。如果你正在准备校招笔试或者想系统自查计算机基础这篇内容应该能帮你省不少时间。1. 算法编码题考场上的时间分配与边界处理网易这类互联网公司的笔试编码题通常不会给一道纯模板题而是喜欢在“常见解法”基础上换一个条件考察你能否快速判断出正确的算法模型。2016这套题里有两道题我印象很深一道是数组相关一道是二叉树相关都属于“看似简单实际上手才发现问题不少”的类型。1.1 有序数组合并求第K大从暴力解到二分排除题目大意是给定两个升序数组 A、B 以及一个整数 K要求找出两个数组合并后的第 K 大的元素。很多人的第一反应是“归并排序的合并过程”直接把两个数组合并到一个临时数组然后按下标取第 K-1 个。这种解法的时间复杂度是 O(mn)空间复杂度也是 O(mn)如果题目没有限制这么写确实能过。但笔试的编码题往往有隐含要求。网易这道题给出的函数签名是int findKth(int A[], int m, int B[], int n, int K)并没有给出时间和空间限制。可一旦看穿出题人的意图就知道他在等你用二分排除法。二分排除的思路是这样的要找两个有序数组的合并第 K 大每次比较 A 数组的第 K/2 个元素和 B 数组的第 K/2 个元素注意下标从 1 开始数假设aMid A[K/2 - 1]bMid B[K/2 - 1]。如果aMid bMid说明 A 的前 K/2 个元素中最多只有 K/2 - 1 个比 bMid 小而 B 中至少有 K/2 个元素大于等于 bMid所以 A 的前 K/2 个元素无论如何都不可能成为合并后的第 K 大可以一次性丢掉。然后问题就缩小成在剩下的 A[K/2:] 和完整的 B 中找第 K - K/2 大。每次丢弃一半时间复杂度降到 O(log(mn))。这里有两个边界条件最容易写错。第一个是当 K/2 大于某个数组长度时就只能取那个数组的最后一个元素或者直接处理另一个数组。第二个是当 K1 时直接返回min(A[0], B[0])。还有一个小陷阱如果某个数组已经空了问题就退化成在单个有序数组里找第 K 个元素直接返回A[K-1]即可。注意笔试里这类“递归 边界条件”的题型除了正确性阅卷还会看你对边界条件的处理。哪怕你用的是 O(mn) 的解法只要边界判断完整得分也不会低但如果你用二分却让某个数组越界扣分会很严重。我当年写的就是先归并再取下标代码量虽然多了但胜在稳。后来复盘时才发现二分排除才是出题人真正想考察的点——因为这道题在 2016 年前后经常作为一面算法题出现放到笔试里其实是在筛掉“只会背模板”的人。1.2 二叉树的完全性判断层序遍历的边界意识另一道编码题是判断一棵二叉树是否是完全二叉树。什么叫完全二叉树除了最后一层之外每一层都被填满且最后一层的节点都靠左排列。这听起来很好理解但很多人写代码时只知道“层序遍历”一旦遇到“左子树为空但右子树不为空”的节点就容易漏判。我当时采用的方案是借助队列做层序遍历在遍历过程中维护一个mustBeLeaf标志。当第一次遇到某个节点没有左孩子或没有右孩子时就把标志置为 true之后再遍历到的任何节点如果它还有左孩子或右孩子就返回 false。还有一种更简洁的写法按层序遍历把所有节点包括空节点依次入队当从队列中弹出第一个空节点后如果队列中还有非空节点说明不是完全二叉树。这两种方法等价但第二种写法更考验对“空节点”的处理代码也更短。我在考场上用的是标志位方法写完后又自己找了几个用例验证。比如一棵只有右孩子没有左孩子的树第一层根节点入队左孩子为空、右孩子非空按完全二叉树的定义直接判负又比如最后一层节点不靠左的树遍历顺序会很自然地暴露问题。这类题之所以高频是因为它把“对数据结构的理解程度”和“对边界条件的敏感度”绑在了一起。很多候选人能写出层次遍历却忘了判断“节点可能缺失孩子”带来的连锁影响。1.3 时间分配策略适可而止网易这套题中算法题大概有三四道除了上面两类还有一道字符串处理题难度都不算变态。我的经验是笔试卷子上每道题的分数权重不一样如果某道题卡了 15 分钟以上还没思路果断先跳把后面会做的题写完再回来。编码手写题最忌讳“死磕一道题导致卷面大片空白”因为阅卷是按点给分有部分正确思路也会给分。2. 操作系统与计算机网络不是死记硬背是场景判断研发工程师笔试中操作系统和计算机网络基本是必考模块。网易这套题的特点是不直接问你“什么是死锁”而是给出一段场景描述让你判断哪几个选项是死锁发生的必要条件。这种问法更贴近真实工作里的问题定位因为你在排查线上进程卡死时面对的就是一堆表象而不是一道定义题。2.1 进程与线程为什么“线程拥有独立栈”不是错误选项有一道经典选择题大概是这样关于进程和线程下列说法正确的是备选项里通常会出现“线程拥有独立的地址空间”“线程拥有独立的栈”“进程间可以通过共享内存通信”“线程切换开销比进程切换大”。这道题的迷惑性在于很多人背过“线程共享进程的地址空间”所以看到“线程拥有独立的栈”就以为是错的。这里一定要区分两个概念线程共享的是进程的地址空间和堆资源但每个线程都有自己的栈和寄存器上下文否则函数调用时的局部变量就无法隔离。所以正确的说法是线程有独立的栈但没有独立的地址空间进程间通信方式包括共享内存、管道、消息队列等其中共享内存效率最高线程切换通常比进程切换开销小因为线程共享地址空间不需要切换页表。如果选项里有“线程拥有独立的栈”它是正确描述不是陷阱如果选项里有“线程拥有独立的地址空间”那才是错误说法。这道题考完很多同学都对答案有争议本质上是把“共享”理解得太绝对。你可以这样记线程之间“共享代码段、数据段、堆”但是“栈和寄存器上下文”是各自独立的。这个细节在后续并行编程里也至关重要尤其是排查栈溢出时要知道每个线程栈的大小是独立分配且有限制的。2.2 死锁的四个必要条件与银行家算法另一个死锁相关题目给了一个资源分配场景要求判断当前系统是否处于安全状态。这其实就是银行家算法的简化版数据规模不大可以用表格手算。死锁发生的四个必要条件互斥、持有并等待、不可剥夺、循环等待。预防死锁的思路就是破坏其中一个条件。我在考场上的做法是先把每个进程还需要多少资源算出来然后模拟分配顺序看是否存在一条“所有进程都能完成”的序列。这个过程不难但很考验细心程度。网易这道题的数据稍微设计了一下初始可用资源给得刚好够某一个进程完成如果你选错了第一个执行的进程后续分配就会卡住。这里有个笔试技巧看到银行家算法的题优先找“当前可用资源能满足哪个进程的剩余需求”然后顺着这个顺序去推。如果有多个进程都能满足随便选一条路径只要证明存在安全序列即可。如果算不出来再检查自己是不是把“已分配资源”和“剩余还需资源”搞混了。2.3 TCP 三次握手与拥塞控制经典但未必全对网络部分考了 TCP 三次握手和拥塞控制的基础概念。三次握手本身不难但题目可能会在选项里混入“第二次握手同时携带 ACK 和 SYN 标志”“第三次握手失败时服务端会发送 RST 报文”这类有争议的细节。我记得那道题考的是拥塞控制机制慢启动阶段拥塞窗口是指数增长的达到慢启动阈值后进入拥塞避免阶段改为线性增长一旦超时阈值减半如果收到三个重复 ACK 则执行快重传、进入快恢复。这几个阶段在《计算机网络》教材里都有但把人放在笔试环境里很容易把“慢启动阈值”和“拥塞窗口”搞混。我的建议是遇到这类题直接画一个时间轴分段1 到 4 轮是慢启动指数增长4 到 8 轮是线性增长第 9 轮超时后阈值减半。一来缩短计算时间二来不容易错。网络部分很多题目其实靠画图就能解决比纯记忆可靠得多。3. C/C 与 Java 语言底层虚构题与送命题网易 2016 年研发工程师笔试题里语言基础部分占比不低。这套题的特色是“知道就是知道不知道就是不知道”几乎没有蒙的余地。尤其是 C 的虚函数机制和 Java 的容器类几乎年年出年年有人错。3.1 构造函数为什么不能是虚函数有一道 C 题目构造函数能否声明为虚函数为什么答案是不能。原因要从虚函数机制本身说起虚函数调用依赖对象的虚函数表指针vptr而 vptr 的初始化发生在构造函数体内。如果构造函数本身是虚函数调用它时虚表还没有初始化系统就无法确定该调用哪个版本的构造函数形成“先有鸡还是先有蛋”的死锁。析构函数则相反通常建议把基类析构函数声明为虚函数否则通过基类指针删除派生类对象时只会调用基类析构函数导致派生类资源泄漏。这道题顺带还会考虚函数表的内存布局单继承下一个对象开头有一个指向虚函数表的指针多重继承下对象可能有多个 vptr。如果有“在构造函数中调用虚函数会怎样”的变体题你要知道构造函数中调用虚函数不会发生多态因为此时派生类部分还没构造完成虚表指针指向的仍然是当前类的虚表。这个考察点在真实 C 开发里很有用尤其是做插件系统和框架设计时。3.2 Java HashMap 与 Hashtable 的对比陷阱Java 部分有一道题关于 HashMap 和 Hashtable下列哪个说法是错误的选项里有“HashMap 允许 null 作为 key”“Hashtable 是线程安全的”“HashMap 的默认容量是16”“Hashtable 允许 null 作为 value”。答案是“Hashtable 允许 null 作为 value”是错误的。Hashtable 既不允许 null key 也不允许 null valueHashMap 则两者都允许。原因是 Hashtable 是 JDK 1.1 就存在的遗留类它通过给整个方法加 synchronized 保证线程安全而 HashMap 设计上就不是线程安全的所以没有这个限制。如果你用 HashMap 做缓存且没有做并发控制在多线程环境下很可能会出现数据错乱这在 2016 年那道题里没直接问但在面试环节经常被追问。还有一道与 HashMap 相关的扩展题HashMap 的扩容机制是怎样的如果了解 JDK7 的链表头插法就会知道并发扩容时可能形成循环链表导致下次查询发生死循环。这个知识点虽然 2016 年笔试题里没有直接出现但作为研发工程师尤其是做服务端的同学今天依然值得深入了解。3.3 语言题的学习方法从“背结论”到“构造场景”我体会最深的一点是语言底层题不能只背结论必须能解释“为什么”。如果你只知道“构造函数不能是虚函数”却说不清 vptr 的初始化顺序面试官会认为你只是背了八股。反过来如果你能画出一个单继承、多重继承的内存布局图即使当时答案写错了面试官也愿意给你机会。4. 概率智力题考察的是建模而不是结果网易笔试比较喜欢在试卷末尾安排一两道概率题或智力题这类题目单看答案可能不难但它考察的是你把现实问题转化成数学模型的能力。2016 年这套题里有一道红球白球取球问题一道抛硬币求期望问题都是那种“看起来像公务员行测实际上考的是状态机”的题型。4.1 红球白球取球问题奇偶性是不变量题目大概是袋子里有若干个红球和白球。每次取出两个球如果两个球同色就放回一个红球如果两个球异色就放回一个白球。问最后剩下来的球是什么颜色我第一次做的时候试图去枚举每一种可能结果发现状态空间很大很快放弃了。后来看标准答案解析才发现这道题的核心是不变量白球数量的奇偶性始终不变。为什么每取一次球袋中球的总数减一。同色红红或白白时取出两个球放回一个红球白球数量要么减 2白白要么不变红红异色时取出红白各一个放回一个白球白球数量不变。因此白球数量的变化只有“减 2”和“不变”两种可能奇偶性永远不改变。最后袋中只剩一个球时如果初始白球数量是奇数那最后一定是白球如果初始白球数量是偶数那最后一定是红球。这个“找不变量”的思维比这道题本身重要得多。我在后来的算法工作中经常遇到类似场景比如判断一个状态的变换是否可逆、在多线程并发中找哪些变量是守恒的本质上都是同一套思路。笔试里出现这种题并不是想难为你而是想看看你有没有“从变化中找不变”的直觉。4.2 抛硬币的期望次数状态转移与方程思想另一道概率题是抛一枚均匀硬币直到连续出现两次正面朝上就停止求抛硬币次数的期望。这道题的常见解法是利用“状态”设方程。设 E0 表示当前已经连续 0 次正面时还需要抛多少次才能结束E1 表示当前已经连续 1 次正面时还需要抛多少次才能结束。从 E0 的状态抛一次如果反面概率 1/2回到 E0 的状态如果正面概率 1/2进入 E1 的状态。所以有E0 1 (1/2) * E0 (1/2) * E1。从 E1 的状态抛一次如果反面回到 E0如果正面游戏结束所以E1 1 (1/2) * E0。联立这两个方程解得 E0 6。这道题如果直接硬算“第一次正、第二次正”的期望很容易算错。而用状态转移方程思路清晰得多。这种思想在后面学习马尔可夫链和排队论时也经常出现尤其做系统性能建模时你会反复用到“当前状态 转移概率 期望方程”的框架。4.3 考场策略先列状态再动笔遇到概率题我现在的建议是先确定问题能用几种状态描述再写出状态之间的转移关系最后解方程。即使最后解不出来把方程写到卷面上也能拿到部分分数。千万不要直接套某个公式概率题的坑在于题目条件一变公式就失效。5. 这套题给后来者的三点启发最后聊一点个人的复盘体会不一定每条都适用于每个人但应该能帮刚准备笔试的同学少走弯路。第一刷题之前先给自己列一份“知识点清单”。网易这套笔试题覆盖的范围很固定数据结构数组、链表、二叉树、算法排序、二分、动态规划、操作系统进程线程、死锁、内存管理、网络TCP/IP、HTTP、语言基础C/Java 底层。我当时就是按这个清单逐个模块复习比漫无目的刷题有效得多。第二笔试看准确率面试看思维过程。同样是编码题笔试时你写出一个能跑通所有用例的完整解法就能拿高分但面试时面试官更关注你如何分析问题、怎么处理边界条件、有没有考虑复杂度和并发安全。所以准备笔试时先保证熟练度如果进入面试环节再刻意练习“讲思路”。第三老题也有价值。2016 年的题目虽然年代久远但核心考点并没有过时。尤其是语言底层和操作系统部分现在面试依然在问只是换了一层皮。把一套经典笔试题吃透远比走马观花刷十套新题更有用。如果你正在准备网易或其他公司的研发岗笔试可以按这套思路做一次复盘先独立做完再对着答案分析每道题背后的知识点最后把错题归类。我当时整理了一个错题本考前只看本子效率很高。希望这篇复盘能帮你少踩一些我当年踩过的坑。
返回列表