2016年春天,我正忙着投暑期实习简历,网易的笔试邀约是在一个周二晚上落进邮箱的。当时对这家公司的印象很具体:游戏、邮箱、考拉,加上那一句“有态度”。笔试安排在周末,在线做,选择题占了大头。我原以为选择题就是走个过场,真正拉开差距的是后面的编程题,结果一套题做下来脸有点疼——选择题的覆盖面和挖坑深度,远超我当时的预期。
后来秋招又见了几家大厂的笔试,回头再看这份网易2016实习研发工程师的选择题,会发现它几乎涵盖了国内互联网公司校招笔试的典型考点:C/C++内存、数据结构、操作系统、网络、概率,再加几道智力题。这套东西到今天仍然有参考价值,不光是题型,而是它背后那种“考你基础密度”的出题思路。这篇文章我就把当年卷子上选择题部分的考点版图、解题思路和踩坑教训整理出来,给正在准备大厂实习笔试的同学一个参考。
1. 选择题部分全貌:不是刷题,是考“基础密度”
1.1 我印象中的题量与时间压力
网易这份实习笔试的选择题数量,各个岗位和批次不完全一样,我当时遇到的是大约二十道左右,分布在C/C++、数据结构、操作系统、计算机网络、概率统计几大板块。题目本身属于“每一道都不算难,但合在一起压力很大”的类型——你刚在二分查找上松了一口气,下一道就已经切到TCP状态转换了。
时间上,笔试是限时的,选择题部分不能在某一题上恋战太久。我当时的策略是第一遍先做会做的,把需要演算或犹豫的题目标记出来,等全部过完一遍再回来补。这个策略帮我避免了一个很常见的问题:在一道概率题上卡了十分钟,后面数据结构题反而没时间看。
1.2 考点分布与分值权重
根据当时同批参加笔试的同学的反馈,结合我自己试卷上的题目印象,考点大致可以分成下面几类。这个比例在不同年份、不同岗位(比如C++研发和Java研发)会有波动,但总体结构是稳定的。
| 考点板块 | 大致占比 | 典型考察方式 |
|---|---|---|
| C/C++语言与内存 | 25%左右 | 指针、sizeof、字节对齐、const/static |
| 数据结构与算法 | 25%左右 | 二叉树、排序复杂度、哈希冲突、图遍历 |
| 操作系统 | 15%左右 | 进程状态、死锁、页面置换 |
| 计算机网络 | 15%左右 | TCP三次握手、HTTP状态码、DNS |
| 概率统计与逻辑题 | 10%左右 | 条件概率、数学期望、智力题 |
| 综合/常识 | 10%左右 | 代码阅读、程序输出结果、简单推理 |
这个分布透露了一个重要信号:网易考的不是某一两门课的深度,而是候选人对计算机基础学科的“覆盖面”。对于实习生岗位,他们默认你项目经验不会太多,所以更看重基础够不够扎实。
1.3 网易这批选择题的筛选逻辑
做过不少笔试题之后,我慢慢理解了大厂选择题的筛选逻辑。网易这套选择题并不追求每道题都难到让人做不出来,而是通过“覆盖面广+干扰项精细”来淘汰基础不牢的人。
举个例子:一道考二叉树遍历的选择题,如果只是问前序遍历结果,那基本是送分题。但网易的题目往往会加一层包装——先给你一个通过层序遍历构建二叉树的序列,再问某种遍历的结果,或者把递归和非递归两种写法的输出放在一起比较。这里考察的不只是“会不会背遍历顺序”,还包括“能不能在脑子里构建树结构”和“对递归栈的理解”。
所以做这类选择题的最高效策略不是刷题,而是把每个考点背后的原理吃透。题目可以千变万化,但底层考点就那么几个。
2. C/C++内存题:最细碎也最能拉开差距的选择题
2.1 sizeof、strlen与指针退化的经典组合
C/C++的选择题里,sizeof和strlen的对比几乎是必考题。这类题表面考的是函数或运算符的用法,实际上考的是对数组名、指针、字符串字面量在内存中表现的理解。
我当年遇到的题目是这样一类变形:定义一个字符数组和指向它的指针,分别用sizeof和strlen去计算,选出结果正确的一项。如果平时只背了“sizeof是运算符,strlen是函数”这个层面的知识,很容易掉进坑里。关键在于数组名在sizeof语境里不会退化,sizeof(数组名)得到的是整个数组占用的字节数;但当数组名作为参数传给函数时,它退化成指针,sizeof得到的就只有指针的大小了。这种“退化的陷阱”在做题时特别容易踩,因为一眼看上去代码里全是同一个数组名。
还有一类变体是字符串字面量。char *p = "hello"和char s[] = "hello",虽然都表示一串字符,但p是指针,sizeof(p)是4或8(取决于平台),sizeof(s)是6(包括末尾的'\0')。如果题目里再混入const char*的修饰,那考察点就又多了一层:只读字符串和可写字符数组在内存区域的差异。
我在答题时的经验是:把一类题的所有变形整理成一张表,每次做题前在草稿纸上快速默一遍。不要高估自己的记忆力,尤其是字节大小这类需要硬记的数字,考前看一遍和考场上能准确想起来是两回事。
2.2 字节对齐:结构体大小的隐藏算法
结构体大小计算是C/C++笔试选择题里一个高频且容易错的点。它考察的是对内存布局的理解,而不是纯粹的算术。
一个典型题目会定义这样的结构体:
struct Test { char c; int i; double d; };如果按“每个成员类型大小直接相加”来算,结果应该是13,但正确答案取决于平台的对齐规则。在常见的64位Linux环境下,默认按8字节对齐,结构体大小会是24(c占1字节,填充3字节后i占4字节,再填充4字节后d占8字节)。Windows上的规则可能又不一样,默认对齐数是8但实际对int按4字节处理。
做这类题的核心是记住两条规则:第一,每个成员的偏移量必须是其自身大小的整数倍;第二,结构体总大小必须是最大成员大小的整数倍。考试时先看有没有#pragma pack(n)这样的编译指令——只要有pack指令,对齐规则就按pack的n值和成员大小的较小值来算。
我当时在这个考点上吃过亏。我记住了默认对齐的规则,但遇到带#pragma pack(1)的题时,下意识按默认规则去算,结果做错了。考试题目里一旦出现这种“强指定”的编译指令,它就是在提醒你:不能用通用规则去套,必须按题干的指令来。
2.3 const、static、volatile:修饰对象的区别
这三个关键字在C/C++面试里经常一起出现,选择题通常用“下列哪种说法正确”的形式来考。它们的题目都不长,但每一条描述都需要你判断得足够精确。
const的核心在于“const修饰的对象本身不可修改”,但要注意区分顶层const和底层const。const int *p意味着p指向的值不能通过p修改,而int *const p意味着p本身不能指向别处。选择题常常把这两种写法放在四个选项里,让你选出哪一句描述才是对的。这里没有捷径,必须把条条框框的语法记清楚。
static的考点就更多了:局部static变量的生命周期、全局static变量的作用域限制、类中static成员变量的共享属性。我印象里有一道题考的是“在函数里定义static局部变量,这个变量存储在哪个段”,答案是数据段(已初始化数据段),不是栈区,也不是堆区。这个考点看起来基础,但不少人在“变量定义在函数内部”和“存储位置在全局区”之间会感到矛盾。
volatile的关键作用被很多选择题包装成“防止编译器优化对变量的读取”。识别它的标志性场景是:变量可能在中断处理函数里被修改,或指针指向内存映射I/O端口。做题时,看到“这个变量会被外部改变”这个语境,正确答案往往就和volatile有关。
2.4 我当时的一个失误:在选择题里想当然
复盘2016年这份试卷时,我自己犯下最典型的一个错误,是一道关于“函数传参”的选择题。题目给了这样一段代码:
void func(char *p) { p = (char *)malloc(100); }然后问调用func(str)之后str的值是什么。正确答案是“str不变”,因为C语言的参数传递是值传递,p在函数内部被重新赋值并不会影响函数外部的str。我当时被“malloc了100字节”这个细节带偏了,选了“str指向新分配的空间”这个错误选项。
事后总结,这类题考的就是“函数形参是实参的一份拷贝”这个朴素的原理。指针本身也是值,传指针进函数,你在函数内修改的是拷贝出来的这个指针变量。想要真正改变外部指针指向,必须传二级指针或者使用引用(C++里)。
这种失误很有代表性——不是不知道原理,而是被题面包装出的“复杂操作”牵着走,忽略了最底层的机制。网易的选择题喜欢用这种方式制造干扰:知识点不难,但选项设置在引导你往想当然的方向走。
3. 数据结构与算法:选项不是白给的,动手演算比心算可靠
3.1 二叉树求深度/节点数:要写递推,不要心算
二叉树相关的选择题,网易出得比较多的类型有:给定某种遍历序列求另一种遍历序列、计算二叉树深度、计算满二叉树/完全二叉树的节点数、判断某棵树是否平衡。
其中“给定前序和后序遍历求可能的中序遍历”这类题非常能体现选择题的价值:它不让你写代码,但要求你在脑子里构建出树的形态。我的做法不太一样——我会直接在草稿纸上画出树的结构,再对照选项验证。哪怕这道题只有一分钟的时间,画图仍然比纯心算可靠,因为树的形态一旦画错,后面全盘皆输。
还有一类高频计算题是完全二叉树的节点问题。例如“深度为h的满二叉树一共有多少个节点”,答案是2^h - 1;而遇到“完全二叉树有n个节点,求叶子节点个数”时,需要利用“最后一个非叶节点的下标是n/2”这个性质。我复习时候的简单记法:二叉树中,叶子节点数等于度为2的节点数加1。知道这个等式,很多看似复杂的节点计算题都会被简化成一道一元方程。
3.2 排序算法的稳定性与复杂度边界
排序算法属于“笔试选择题里的基本功”。网易这块的出题风格不是直接问“快排平均复杂度是多少”,而是给出一个具体场景,让你判断哪种排序适合。
比如我印象里有一道题是“对几乎已经有序的数组排序,最高效的算法是哪一种”。答案是插入排序,因为当元素基本到位时,插入排序的比较和移动次数都很少。堆排序、快速排序在这类数据上反而可能产生较多无效操作。快速排序最坏情况下退化为O(n²),这个特性也是选择排序类算法的常见干扰项来源。
稳定性也是高频考点。选择题往往给出“下列哪些排序算法是稳定的”这种组合选项。稳定算法包括插入排序、冒泡排序、归并排序、基数排序;不稳定算法包括选择排序、希尔排序、快速排序、堆排序。这串名单背下来不难,但要理解稳定性在工程里的意义:当你有多个排序关键字时,稳定排序会保留前面排序的相对顺序。
我推荐一个复习方法:把每种排序算法按照时间复杂度(最好、平均、最坏)、空间复杂度、稳定性三列做成一张表,考前花十分钟扫一遍。比反复做选择题效率高得多。
3.3 哈希冲突与负载因子:靠直觉判断性能
哈希表是笔试选择题里的常客。绕着哈希展开的考点就三个:哈希函数、冲突解决、负载因子对性能的影响。
冲突解决方式的题目最容易出辨析。开放定址法和链地址法的区别在于,前者在冲突时寻找下一个空槽,后者在同一个槽位挂一个链表。网易的题目如果给出一个哈希表的插入序列和表长,通常会让你计算“平均查找长度”。这种题没有技巧,老老实实按冲突解决规则在纸上模拟插入过程。做题时要注意题干用的是“成功查找”还是“不成功查找”,两者计算细节不同,选项往往故意混淆。
负载因子的理解可以靠直觉:装载因子等于表中元素数除以表长。因子越大,冲突概率越高,查找效率越低。C++的unordered_map、Java的HashMap在负载因子超过阈值时都会自动扩容,这是工程中哈希表性能保持稳定的关键。选择题如果问“哈希表扩容后,原有元素需要如何处理”,答案一定是“重新计算哈希并分配位置”,而不是原地不动。
3.4 图的最短路径:Dijkstra与Floyd的适用场景
图相关的选择题在实习生试卷中出现频率不低。Dijkstra适合单源正权图,Floyd适合多源全源图,Bellman-Ford能处理负权边。选择题不会要求你写出完整算法过程,但会问“下列哪种算法适用于本场景”。
我遇到过一道题,问你“要在有向图中找到每一对顶点之间的最短路径,选择哪个算法”。答案当然是Floyd。干扰项往往是Dijkstra和Bellman-Ford,因为它们也广为人知。解题的关键是看“每一对顶点”这个关键词——只要出现这个表述,目的就是要用矩阵动态规划的思路,而不是做n次单源。做这类题要训练对场景关键词的敏感度:单源、多源、负权、稀疏图、稠密图,每个词都是选型的信号。
4. 操作系统与计算机网络:概念题里带着场景,需要二次翻译
4.1 进程状态机与死锁的“四个必要条件”
操作系统选择题里,进程状态转换和死锁是最稳定的两个热门考点,网易也不例外。
进程五状态模型的典型题目是:一个进程等待I/O完成,此时进程处于什么状态。答案是阻塞状态。如果我们用“等待资源/事件”这个框架来推:等待CPU时间片是就绪态,等待I/O是阻塞态,等待调度分配CPU才是运行态。这三者的界限,选择题特别爱混着出。我的经验是把状态转换图记牢:就绪->运行,运行->就绪,运行->阻塞,阻塞->就绪。注意没有阻塞->运行这种直接转换。
死锁的四个必要条件是互斥、持有并等待、不可剥夺、循环等待。我知道考试时会问“下列哪些措施可以预防死锁”,答案永远离不开破坏这四个条件。例如“一次性申请全部资源”破坏了持有并等待,“允许剥夺”破坏了不可剥夺。做题时把每个选项翻译成“它破坏了哪个条件”,正确率会大幅提升。这个翻译能力本身也是面试官希望从选择题里看到的。
4.2 页面置换算法:Belady异常这种题怎么快速判断
虚拟内存的页面置换算法也是操作系统选择题的高频区。最常考的是FIFO、LRU、OPT。
FIFO有一个特殊性质:Belady异常——增加页框数反而导致缺页次数增加。选择题如果问“哪种置换算法可能出现Belady异常”,答案是FIFO。LRU和OPT都不会出现。这里不需要死记结论,可以理解背后的原因:FIFO的淘汰顺序和进程的实际访问模式没有任何关系,纯粹按进入顺序来,页框增多时可能把一个即将访问的页面提前淘汰出局。
如果需要实际计算缺页次数,我的建议是按“访问页面序列 + 页框内容表”一步步画出来,每一步标记是否缺页。不要试图在大脑里推演全过程,手写表格最稳妥。选择题的干扰项常常是“正确次数+1”或“正确次数-1”这种细微偏差,只有老老实实画一遍才不会出错。
4.3 TCP三次握手与“连接断开”的行为选项
计算机网络在选择题里占的比重大概是15%,其中TCP协议的内容最常见,围绕三次握手展开的题目尤其多。
一类典型题是:“TCP建立连接时,第二次握手是客户端还是服务端发出的?标志位是什么?”第一次客户端发SYN,第二次服务端发SYN+ACK,第三次客户端发ACK。记住这个流程不难,但题目会以变体形式出现,比如问“第二次握手如果丢失,客户端和服务端各处于什么状态”。答案中服务端会等待客户端的ACK,超时后重传SYN+ACK,同时服务端这个半连接可能进入SYN_RECEIVED状态。客户端则可能因为长时间没收到服务端的响应最终超时。这类情景题考的不只是“记住三次握手”,还涉及对状态和超时机制的理解。
TCP四次挥手同样值得注意。主动关闭方发送FIN后进入FIN_WAIT_1,收到对方ACK后进入FIN_WAIT_2,等对方发送FIN并回复ACK后进入TIME_WAIT。TIME_WAIT的时长和存在的意义也是一个经典选项。选择题如果问“为什么要保持TIME_WAIT”,常见理由是确保最后的ACK能到达对方,而不是立刻关闭端口。
4.4 学会用排除法做概念题
计算机网络部分有几道题考察比较偏记忆性的知识,比如HTTP状态码、DNS解析流程。这类题没有太多逻辑推理的余地,更像是送分题。但网易的出题方式通常是“给出四种描述,选正确的”。
做这种题,排除法比直接找正确答案要快得多。例如HTTP 301是永久重定向,302是临时重定向;DNS的解析流程是先从本地浏览器缓存查,再到本地DNS服务器,再到根DNS服务器,逐级向下。如果选项里说“DNS请求总是先发给根服务器”,那就错了,因为本地缓存优先于根服务器。用排除法,每排除一个错误选项,成功率就提升不少。
我在这一板块的经验是:计算机网络考察的是“你知道这个机制存在,并且知道它大致怎么运作”。选择题不需要你把协议细节背到一字不差,但要对机制有整体把握,尤其是TCP状态、HTTP语义、DNS层次关系这三角色。
5. 概率统计和逻辑题:选择题里少数的“计算题”
5.1 条件概率题:先画样本空间
概率统计在试卷里占的比例不高,但几乎每次都会出现一到两题。它们的特点是有明确计算过程,且在短时间内不容易搞定。
条件概率是关键考点。一个典型的题目是:“一个袋子里有3个红球和2个蓝球,不放回连续取两次,已知第一次取到红球,求第二次也取到红球的概率。”这类题的正确做法是先缩小样本空间:第一次取到红球后,袋子里剩下2红2蓝,所以第二次取到红球的概率是2/4=1/2。但如果不加思索套用乘法公式,很容易算错。
我自己的做题经验是:碰到任何条件概率题,先在草稿纸上写“在……发生的情况下”,然后重新计算剩余样本的数量。这种方式虽然朴素,但能保证不被公式绕晕。选择题里条件概率的正确项往往是很简洁的一个分数,如果算出来的结果是分数连乘的复杂形式,通常会怀疑一下是不是题目条件理解错了。
5.2 数学期望:逐项加权别跳步
数学期望的题也常出现,不过网易很少直接考“给出概率分布求期望”,更多是披着实际场景的外衣。比如:“某人连续投篮,每次命中率为p,直到投中为止,求投篮次数的期望。”这是典型的几何分布,期望是1/p。
如果题目更复杂一点,比如“投中后获得积分,未命中继续投,总积分是一个函数”,就需要把不同情况的期望贡献分开算。我在复习概率论时受到的训练是:列一个表,左边是可能的取值,右边是概率,然后用Σ(p_i * x_i)去算。这个过程看起来笨拙,但正确率最高。
选择题最坑的地方在于,它会把“可能得分的项”和“概率”故意写得混乱,如果你不按表格来,很容易漏掉某一项。把每一步都写在草稿纸上,比在心里默算更安全。
5.3 经典博弈/智力题:标记后跳过的策略
智力题这部分,网易偶尔会放一两道经典的脑筋急转弯或博弈题。它们的共同特征是:乍看简洁,但推理链条较长,特别消耗时间。
我印象中的典型智力题形式有:猜帽子颜色、两个人轮流取石子、烧绳子计时等。这种题如果没有在三分钟内想出思路,我会标记为待定,先跳到后面的题目。原因很简单:智力题在选择题里的分值权重并不高,一旦卡住,整个答题节奏就失控了。
如果你在准备这类题,可以提前熟悉几个经典模型:Nim博弈、二分法思想、鸽巢原理的应用。“烧两根绳子计时45分钟”这类经典智力题,即使原题不出现,它的变形也能借助已有思路解决。
5.4 我控制时间的经验:每道概率题不超过3分钟
2016年那份卷子,我在概率和智力题上用得时间比较多,后来总结出一个更合理的策略:选择题部分,每道概率计算题给自己最多3分钟,智力题最多3分钟,超过就标记跳过。因为后面还有编程题,小题过度投入会导致大题的思考时间被压缩。
做题顺序上,我后来也倾向调整为:先做C/C++、数据结构、操作系统这些“确定性高”的题,再做概率题和智力题。确定性高的题往往一眼就能判断对错,能快速积累得分;概率题和智力题受状态影响大,放在最后做反而能利用全卷剩余的时间冷静思考。
6. 整套选择题复盘后的几点实在建议
6.1 做选择题的三个层次
参加过几次笔试面试之后,我发现做选择题其实可以分三个层次,很多人停留在第一层。
第一层是“背答案”,也就是见过某道题,记住了正确选项。这种方式在遇到原题时有效,但题目稍加变形就失灵。第二层是“背考点”,知道某个知识点常考,能根据知识去推导答案。这是大部分认真复习的人能达到的状态。第三层是“理解考点背后的工程含义”,比如知道哈希表的负载因子是为了控制性能,知道TCP的TIME_WAIT是为了可靠性。到达第三层的人,即使遇到没见过的新题,也能根据原理快速分析出答案。
网易这份2016年实习研发工程师选择题,考察的核心就是“你是否到达第二层和第三层之间”。因为实习生的项目经验普遍有限,选择题是测量基础知识体系完整度的最直接方式。
6.2 考场上识破“干扰项”的通用方法
做选择题时,我总结出一个通用的三遍法:第一遍读题干,划出关键词(尤其是“不正确”“错误的是”“会导致”这种否定词);第二遍看选项,把每个选项翻译成自己理解的语言;第三遍,把选项和题干关键词一一验证。
干扰项的设置往往遵循几个规律:把正确的数值偏移一个单位;把两个容易混淆的概念调换位置;在否定词上做文章。如果你发现某道题的选项里有两个“看起来很对”,那通常意味着题干里有某个限定条件被忽略了。往回读题干,把限定条件圈出来,往往能找到破绽。
还有一个非常实际的提醒:在线笔试界面的选项顺序可能会被打乱,千万不要用“上一道题的B选项是正确答案”这种经验主义来推断下一题。每道题都要独立判断。
6.3 这份卷子对我后面笔试面试的长期影响
2016年笔试结束后,我后续又参加了其他几家公司的实习生招聘,发现很多题目都似曾相识。不是题目本身重复,而是考点重复:字节对齐、死锁条件、TCP状态、二叉树遍历。网易这份卷子上的每一个知识盲区,最后都在其他地方以不同形式重新出现过。
这也是为什么我后来一直建议准备校招的同学,不要把笔试看成“做完就结束”的任务。每次笔试结束,把错题对应的考点整理到一份文档里,标注出“当时为什么错”和“正确思路是什么”。这份错题文档的价值会随着你做过的试卷增多而指数级放大。
我自己的体会是:基础题的意义不在那一两分,而在于帮助你建立知识的对照表。数据结构、操作系统、网络,每一科的核心概念就那么几十个,选择题只是一种高效的检验方式。你不需要题海战术,但需要每做一道题都能定位到自己知识体系中的薄弱环节。
最后分享一个小技巧:考前可以准备一份“一页纸考点清单”,把每个板块最容易考、最容易记混的知识点浓缩成关键词。比如在操作系统那张纸上写下“互斥、持有并等待、不可剥夺、循环等待”,在网络那张纸上写下“SYN、SYN+ACK、ACK、FIN”。考前一小时不再刷题,只看这份清单。这比临时抱佛脚做新题有用得多。笔试拼的不只是储备,还有状态,一个好的收尾动作能把你的知识密度在考场上发挥到最大。