前几天整理移动硬盘,翻到自己当年备战360公司2016研发工程师笔试题时存的笔记和草稿,索性花了一晚上重新梳理了一遍。说实话,那个年代的笔试题放到今天来看,核心板块其实没怎么变——C/C++、数据结构、操作系统、计算机网络这几座大山,依然是各大厂校招笔试的固定主角。这篇文章不打算给你堆一个“背答案式”的题库,而是想把2016年这套题背后的考查逻辑、典型考点、解题思路和踩坑经验完整拆开,给正在准备校招或者想补基础的朋友做一个参考。
如果你正在准备技术岗笔试,尤其是偏向C/C++方向、Linux后台、嵌入式这类岗位,那这篇文章至少能帮你少走两个星期的弯路。就算你目标不是安全方向,里面的数据结构、操作系统、网络题解析也是通用考点,看完可以直接用到其他厂的笔试准备里。
1. 2016年360研发笔试的整体考情与题型分布
1.1 考试形式与时间分配
360那几年校招笔试采用的是在线笔试系统,题目分成两部分:客观题和编程题。客观题涵盖单选题和多选题,大概有20到30道,考察范围很杂,从语言基础到网络协议、操作系统原理都有。编程题一般是两道到三道,限定时间在一个小时内完成,部分场次支持本地IDE调试后上传代码。
时间分配上,整套试卷的时长通常在90分钟左右。这个时间其实相当紧张,客观题如果一道题卡住超过三分钟,你就得立刻做标记跳过,否则后面的编程题基本来不及写。我当年就吃过这个亏——在一道多重继承的C++题上纠结了五分钟,结果最后一道编程题只写了一半。
整套试卷的顺序一般是先客观题后编程题,但做题顺序我的建议是反着来:先快速把编程题扫一遍,如果有一道是你能秒解的(比如字符串处理或简单数据结构题),先花20分钟把它拿下,再回头啃客观题。原因是编程题的分数占比高,而且是按测试用例给分的,能过一部分用例就能拿一部分分,比客观题的非对即错要“划算”得多。
1.2 题型构成与分值侧重点
2016年那会儿的笔试分值大概是这样分布的:单选每题1到2分,多选每题2到3分,编程题每题20到30分。客观题总分大概占了60分上下,编程题40分上下。
有意思的是,多选的少选通常会得一半分,但选错就直接归零。这就需要你在答题时拿不准的选项宁可少选也不要乱选。比如一道关于进程间通信的多选题,确定信号量和共享内存是对的,管道不确定,那就果断只选前两个;如果手滑把不确定的管道也选了,遇到正确答案里没有它,这题就白送了。
从知识点权重来看,数据结构和算法大概占了35%,C/C++语言基础占25%,操作系统和网络各占15%,剩下10%是智力题、逻辑推理和数学题。这个比例其实反映了一个导向:笔试阶段他们主要想筛掉“基础不扎实”的人,而不是选出“算法竞赛选手”,所以你把基础题吃透,成绩就不会差。
1.3 知识点覆盖范围速览
我按当年的实际考试范围整理了一张表,你可以对着自查:
| 知识模块 | 典型考点 | 出现频率 |
|---|---|---|
| 数据结构 | 链表、二叉树遍历、栈与队列、哈希表 | 极高 |
| 排序与查找 | 快排、归并、堆排、二分查找变种 | 极高 |
| C/C++语言 | 指针、内存对齐、const/static/volatile、构造函数析构顺序 | 极高 |
| 操作系统 | 进程线程、死锁、内存分页、页面置换 | 中高 |
| 计算机网络 | TCP三次握手、状态码、DNS、HTTP | 中高 |
| 算法思维 | 动态规划、贪心、递归转迭代 | 中 |
| 智力题 | 逻辑推理、概率、最优化问题 | 中低 |
这里提醒一句:不要因为“智力题占比低”就完全放弃。真实考试里往往就靠那么一两道智力题拉开分差。而且这类题一旦你掌握了出题套路,反而是全场最稳定的得分点。后面我会专门讲。
2. 核心考点拆解:他们到底想考什么
2.1 C/C++与内存管理:笔试的第一道分水岭
360作为安全起家的公司,对C/C++的重视程度远超一般互联网公司。笔试里经常出现一类题:给一段代码,让你判断输出结果,或者指出哪里会导致崩溃。这类题表面考语法,实际上考的是你有没有真正理解“内存里发生了什么”。
举一个高频考点——内存对齐。题目一般长这样:
struct A { char a; int b; char c; };问你sizeof(struct A)是多少。如果你回答“1+4+1=6”,那这道题就废了。在32位系统默认4字节对齐下,结构体A的实际大小是12字节:char a占1字节后填充3字节,int b占4字节,char c占1字节后再填充3字节到下一个4字节边界。
同样的成员换个顺序:
struct B { char a; char c; int b; };大小就变成了8字节,因为char a和char c紧挨着占2字节,再填充2字节到4字节对齐边界,然后int b占4字节。我当时复习时总结了一句口诀:把结构体成员按占用空间从大到小排列,能省不少内存——这不只是笔试技巧,在实际工程里对缓存友好性也有帮助。
还有一类必考题是C/C++中const、static、volatile的辨析。const是只读,但注意const变量不一定就放在只读区,取决于你定义的是普通const还是const字面量;static修饰局部变量时延长生命周期到程序结束,修饰全局变量时限制作用域到当前文件;volatile是告诉编译器“这个变量可能被外部改变,别优化掉对它的读取”。这三者单独拎出来都不难,但混在一道题里问就特别容易出错,尤其是“const char* p”和“char* const p”的区别,前者是指向常量的指针,后者是常量指针本身不能改指向。
2.2 数据结构与算法:拉开差距的重头戏
数据结构这块,笔试出现频率最高的是链表和二叉树,因为它们能用代码填空题、选择题、编程题三种形式轮番考。比如链表反转,在2016年那套题里就出现在选择题(给一段递归代码问输出)和编程题(手写反转)两个位置。
链表反转的迭代写法是常规操作,我建议你把递归版本也练熟,因为笔试考的是“理解”,不是“背代码”。递归版本:
ListNode* reverseList(ListNode* head) { if (head == nullptr || head->next == nullptr) { return head; } ListNode* newHead = reverseList(head->next); head->next->next = head; head->next = nullptr; return newHead; }关键是理解递归走到链表尾部时才开始反转,所以递归深度等于链表长度,过长的链表会导致栈溢出——这个细节经常被拿来当多选题的干扰项。
二叉树题最爱考的是前序/中序/后序遍历的变种,比如“已知前序和中序,重建二叉树”或者“层序遍历输出每层节点”。这类题需要你熟练掌握递归框架和队列的配合。
排序算法那块,笔试很少让你直接写快排,更多是考你“给一个特定输入,写出快排第一趟的结果”或者“归并排序和快排在各种情况下的复杂度对比”。复杂度表格必须烂熟于心:快排平均O(n log n)、最坏O(n²),归并稳定且始终O(n log n)但需要额外O(n)空间,堆排空间O(1)但不稳定。有一年还考了“在100万个元素中找出最大的100个”,最优解是用大小为100的小顶堆,复杂度O(n log 100),这个思路在Top K问题里几乎是标准答案。
2.3 操作系统与网络基础:容易被忽略的送分题
操作系统和网络在客观题里大概占30%的分数,但很多人因为“觉得简单”反而丢分严重。这两块的考点非常固定,只要把原理理解透,是全场最稳定的得分板块。
进程和线程的区别几乎是年年必考。360的题喜欢往深处问——比如“线程之间共享什么,不共享什么”。共享地址空间、文件描述符、信号处理器,不共享栈和寄存器。这里有个坑:虽然线程共享地址空间,但每个线程的栈空间是独立的,所以局部变量在线程间不共享,而堆上的变量是共享的。如果题目问你“两个线程同时修改一个全局int变量,最终结果是什么”,答案是“不确定”,因为并发修改存在竞争条件,需要加锁或使用原子操作。
死锁那块的考查方式通常是:给四个条件,让你选哪些是死锁的必要条件(互斥、持有并等待、不可剥夺、循环等待);或者给你一段伪代码,判断是否可能死锁。这里我教你一个秒杀技巧——看到一个多线程/多进程的加锁顺序不一致的题,答案大概率涉及死锁。解决办法是让所有线程按同样的顺序加锁,或者用trylock避免长时间阻塞。
网络部分最经典的一道题就是“TCP为什么要三次握手,两次不行吗”。这道题的深层答案是:三次握手的根本目的是让双方确认彼此的收发能力都正常,同时同步初始序列号。如果只握手两次,可能会出现客户端发出已失效的连接请求,服务端误以为是新的连接请求而建立连接,白白浪费资源。因为网络中有可能延迟的旧连接请求到达服务端,两次握手无法让服务端区分这是不是一个新的连接请求。三次握手允许客户端收到服务端的确认后,再发送一次确认,从而避免这种历史失效连接。
还有一类题是HTTP状态码,比如问404、500、301、502分别代表什么。这里有个规律:4XX是客户端错误,5XX是服务端错误,3XX是重定向。考得深一点的会问301和302的区别——301是永久重定向,302是临时重定向,涉及浏览器缓存行为,前者会缓存跳转,后者默认不缓存。
2.4 智力题与逻辑推理:考察思维的灵活性
很多技术岗的同学对智力题深恶痛绝,觉得“这跟写代码有什么关系”。但360这类安全背景的公司确实喜欢用智力题考察应聘者的思维建模能力——你能不能用抽象的方式看一个具体问题。
2016年那套题里我印象比较深的一道逻辑题是:有8个球,其中一个比其他的重,给你一个天平,最少称几次能找出重的那个。答案是2次:先分成3、3、2三组,称3对3,如果平衡就说明重的在剩下的2个里,再称一次就知道;如果不平衡,重的在比较重的那一组3个里,再在这3个里挑两个称,同样称一次就能确定。
这种题的套路就是“信息论”思路——每次称重有三种结果(左重、右重、平衡),所以每次最多能区分三种情况。n次称重最多能区分3^n种情况。8个球需要至少2次,因为3^1=3不够,3^2=9足够。掌握这个思路,再遇到“12个球,其中一个重量异常,最少称几次”这种升级题,你就知道怎么入手了。
还有一类常见的概率题:两个人轮流抛硬币,先抛出正面的人赢,问先手赢的概率。答案是2/3。思路是用等比数列求和,或者更直观地理解——先手在第1轮赢的概率是1/2,如果前两轮都失败(两人都没抛到正面,概率是1/4),则局面重置,回到起点。所以先手赢的概率P满足P = 1/2 + (1/4)P,解得P = 2/3。
智力题的备考策略是不要刷海量题目,而是把每一类题的数学本质搞懂。因为笔试现场遇到的题基本不可能和你刷过的题一模一样,大概率是换了个场景的变种。
3. 高频题目复盘:从真题示例看解题套路
3.1 经典算法题:数组与指针的“老熟人”
360的客观题特别喜欢考数组和指针纠缠不清的问题,因为这是区分“背概念”和“真理解”的试金石。我拿一道典型的题目来复盘:
char *str = "hello"; char str2[] = "hello"; str[0] = 'H'; // 选项A str2[0] = 'H'; // 选项B问你哪个操作会导致程序崩溃。答案是A。原因是指针方式定义的字符串,字面量存放在只读常量区,修改它是未定义行为;而数组方式定义的是栈上的一块可写内存,所以可以修改。这道题在考场上错的人一大片,因为很多人只记住了“字符串不能修改”,却分不清哪种方式定义的不能修改。
顺带说一句,这类题在笔试中还有个变种:strlen(str)和sizeof(str2)分别是多少。前者是字符串长度(不包括结尾的'\0'),后者是整个数组大小(包括'\0')。很多人在这里混淆了strlen和sizeof的语义,每年都能收割大量分数。
再看一道二分查找变种题:在一个有序数组中查找第一个不小于target的元素,返回下标。这个其实就是C++里lower_bound的实现。
int lower_bound(vector<int>& nums, int target) { int left = 0, right = nums.size(); while (left < right) { int mid = left + (right - left) / 2; if (nums[mid] < target) { left = mid + 1; } else { right = mid; } } return left; }注意几个细节:mid的计算用left + (right - left) / 2避免溢出;right的初始值是nums.size()(表示区间右开);搜索过程始终保持左闭右开区间[lef, right)。这套模板记住之后,二分查找的所有变种(找第一个、找最后一个、找插入位置)都能套用,比现场推边界条件稳妥得多。
3.2 编程实战题:一道典型的字符串处理题
2016年的编程题有一道和我后来在别的厂见到的题很像:给定一个字符串,把里面的空格替换成“%20”。这题看起来很简单,但如果要求不能用额外空间,必须在原字符串上操作,就有点意思了。
正确的做法是双指针从后往前扫描:
void replaceSpace(char* str, int length) { if (str == nullptr || length <= 0) return; int oldLen = 0, spaceCnt = 0; while (str[oldLen] != '\0') { if (str[oldLen] == ' ') spaceCnt++; oldLen++; } int newLen = oldLen + 2 * spaceCnt; int i = oldLen, j = newLen; while (i >= 0) { if (str[i] == ' ') { str[j--] = '0'; str[j--] = '2'; str[j--] = '%'; } else { str[j--] = str[i]; } i--; } }为什么要从后往前而不是从前往后?因为从前往后替换时,每遇到一个空格都要把后面的字符整体后移,最坏情况是O(n²)时间复杂度。而从后往前只需要一次遍历,是O(n)。这个“先统计长度,再从后往前填充”的套路,在归并排序的原地合并、快排的partition里都能看到类似思想。
考场上写这类题,我强烈建议你先把思路写成注释再写代码。一方面帮你自己理清逻辑,另一方面就算代码没写完,阅卷方看到清晰的注释和思路也能给步骤分。当年我就是靠这个习惯,在一道没完全调通的编程题里拿到了大部分分数。
3.3 选择题的陷阱:别被“熟面孔”带跑
选择题里的技术题最坑的地方在于,题目本身不长,但每个选项都是精心设计的陷阱。比如关于TCP协议的题,四个选项可能分别是“TCP支持广播”“TCP是面向字节流的可靠传输”“TCP头部有源端口和目的端口”“TCP可以全双工通信”,问你哪个是错的。答案是A,因为TCP是面向连接的端到端协议,不支持广播,广播是UDP的用武之地。
再比如操作系统内存管理的单选题:页式存储管理的优点是什么。选项里有“无外部碎片”“有外部碎片”“平均分配内存”“无需硬件支持”。正确答案是“无外部碎片”。页式管理按固定大小的页分配内存,产生的碎片最多是一页以内,属于内部碎片,不会产生随着分配释放而累积的外部碎片。这个知识点在段页式结合的问题里也会反复用到。
所以复盘这类题的思路是:不要只看对错,要把四个选项为什么对、为什么错都整理出来。因为考试中很可能换一种问法、调换选项顺序,本质还是考同一个知识点。你每道吃透的题,相当于帮你覆盖了周边两到三个考点,效率是非常高的。
3.4 动态规划与状态转移:现场推公式的能力
动态规划在2016年的题目里虽然不是压轴题,但在选择题和编程题中都出现过。有一道让我印象深刻的题是一个“数塔问题”的变种:从三角形顶端出发,每次只能走到下方相邻的节点,求路径上数字之和的最大值。
第一反应是递归,但纯递归的复杂度是O(2^n),数据量一大就超时。正确解法是从下往上做DP,用dp[i][j]表示从第i行第j列出发的路径最大值,状态转移方程是:
dp[i][j] = num[i][j] + max(dp[i+1][j], dp[i+1][j+1])从最后一行倒推上来,最终答案是dp[0][0]。这种“反向思维”在动态规划里很常见——从终态往前推,避免子问题的重复计算。
说到面试准备,动态规划我觉得最关键的不是背题,而是学会识别“这题是动态规划”。一般特征是有重叠子问题、有最优子结构、状态只和前面几个状态相关。这三个特征一旦识别出来,哪怕一时推不出转移方程,你也可以先写一个记忆化搜索的版本——笔试的判题系统可能不要求极致性能,但能保证答案正确。等时间充裕再优化成递推。这招在时间紧张的笔试现场非常实用。
4. 备考建议与实战经验:怎么准备才能稳
4.1 时间规划与复习路径
如果你离笔试还有一个月,比较合理的规划是:第一周围绕C/C++基础和数据结构打底,第二周集中刷操作系统和网络题库,第三周专门练算法题编程,第四周做全套的模拟题和错题复盘。
打底阶段,我推荐把《C++ Primer》里关于指针、内存、面向对象的部分仔细过一遍,同时配合做题。只读书不刷题的效率很低,因为笔试考的是“识别陷阱”的能力,这种能力只能通过见识足够多的陷阱来培养。
刷题平台方面,可以循序渐进:先在牛客网上做企业真题,这部分最有参考价值,因为题型和风格跟真实笔试最接近;然后去力扣按标签刷题,比如数组、链表、字符串、动态规划各20道;最后再回到企业题库进行模拟考试。重点不在于数量多,而在于覆盖考点全面。
4.2 考场上如何分配时间和做题顺序
我总结了一套时间分配策略,你可以根据自己的情况调整。客观题控制在45分钟内,平均每道题1分半钟。如果某题超过3分钟还没思路,立刻标记跳过,因为客观题大概率不会因为一道题决定你是否进面试,但编程题空着才是致命的。
编程题建议从最简单的开始做。笔试系统里有“题目列表”,先点开每道题都扫一眼,按你熟练度排序,稳的先拿下。每道编程题留15到20分钟,包括读题、写代码、跑测试用例的时间。尤其要留2到3分钟检查输入输出格式——有些笔试系统要求自己处理多行输入,格式不对,代码逻辑再对也过不了测试用例。
另外,一个很多新手忽略的点:笔试系统里一般支持“保存并调试”,每写完一部分逻辑就运行一次,尽早暴露语法错误和边界错误。不要等全部代码写完才跑,那时候出问题排查起来非常费时间。
4.3 现场应对:遇到完全没头绪的题怎么办
客观题遇到完全不会的知识点,秒选一个看起来最合理的答案然后走人,不要纠结。编程题遇到完全没思路的难题,至少写一个暴力解——哪怕时间复杂度很高,只要小数据量能过,就能拿到一部分分数。
举个例子,某道编程题要求“找出数组中最长的递增子序列长度”,如果你不熟悉动态规划,至少可以写一个两层循环的暴力DP,O(n²)复杂度,对1000以内的测试数据是完全能过的。很多笔试系统会给部分分数,O(n²)能拿到60%左右的用例分。而如果你非要现场想O(n log n)的贪心+二分解法,很可能20分钟过去连代码都没写出来,一分都拿不到。
这个“先暴力骗分,再优化”的思路,是我当年刷笔试总结出来的最实用的考场策略。笔试的核心目的是拿分进面,不是让你秀操作。能跑通的暴力解永远比没跑通的优化解值钱。
4.4 笔试之后的衔接:把卷子变成面试素材
笔试结束后,先别急着对完答案就扔一边。把你做错的、犹豫的、蒙对的题都标记出来,复盘一遍,这比刷十道新题收获更大。因为笔试题目往往就是面试官喜欢的追问方向——我面试时就被问过“你笔试里那道内存对齐的题,能不能说说不同编译器下结果有什么不同”。这说明面试官手里就拿着你的笔试卷子,已经画好了他要追问的重点。
所以复盘时,每个考点都要多问自己一层“为什么”。比如内存对齐,为什么要对齐?因为CPU访问对齐内存更高效,现代CPU一次能读4或8个字节,如果数据跨了边界就要读两次。这种“原理级”的理解,在面试中一旦说出来,比单纯背答案要加分得多。
另外,笔试中出现但你不会的知识点,一定要去查明白,不要因为笔试结束就放过它。你可能在面试中再次遇到,而且面试官会问得更深。
5. 常见失分点与避坑指南:这些坑我替你踩过了
5.1 非技术失分:细节决定你能不能进面
很多人以为笔试挂掉是因为技术不行,其实相当一批人挂在非技术性失误上。
第一个坑是审题不清。笔试题目里明确写了“程序输出结果保留两位小数”,结果你没看到,直接打印了原始double值,扣分没商量。我建议读完题目后用笔把关键要求圈出来(在在线笔试系统里高亮标注),写代码前再确认一次输入输出格式。
第二个坑是环境不熟悉。有些笔试系统是纯网页代码编辑器,没有智能提示,也不支持本地运行的自动补全。如果你从来没在这种环境里写过代码,考场上光是敲C++头文件和main函数模板就会比平时多花好几分钟。提前在牛客或者是笔试平台模拟一次完整的在线笔试流程,特别有用。
第三个坑是时间分配完全失衡。我见过有同学在客观题上硬磕60分钟,编程题只剩20分钟,最后试卷提交时有两道编程题是空白的。即使客观题全对,分也很难过线,因为编程题分占比太高了。
5.2 技术细节的典型错误:常见误区盘点
技术类失分集中在几个“看起来会,实际上不会”的细节上。
第一个是排序算法的稳定性判断。插入排序、冒泡排序、归并排序是稳定的,选择排序、快速排序、堆排序是不稳定的。很多人记混了快排的稳定性。我用一个“代价法”帮你记忆:稳定排序就是相等元素的相对顺序不乱,而归并排序在merge时只要写成if (nums[left] <= nums[right])就能保持稳定;快排的partition过程长了很容易让相等元素交换位置,所以不稳定。
第二个是二分的边界检查。也是笔试编程题最常见的bug来源,把left = mid + 1写成left = mid导致死循环,或者right初始值到底该取nums.size() - 1还是nums.size()搞不清楚。我的解决方法是固定使用一套模板,比如左闭右开区间的写法,然后所有二分题都用它,不去临时推理边界条件。
第三个是C++ STL接口的误用。比如vector在遍历过程中插入或删除元素导致迭代器失效;map的operator[]在key不存在时会插入一个默认值,如果只是查找,应该用find。这些细节在选择题里经常作为“哪个说法正确/错误”的选项出现,笔试前一定要把STL几个常用容器的底层实现和注意事项过一遍。
5.3 如何利用笔试复盘提升后续面试表现
笔试复盘的价值远不止“搞懂这道题”。我在准备360笔试那段时期养成了一个习惯:用一页纸记录每个错题涉及的知识点和我的错因,比如“没考虑空指针”“边界条件写错”“对二分模板不熟”。考前翻一遍,效果比重新刷题好得多。
这个方法后来在面试中也帮了我大忙。因为面试官问的技术问题,很多其实都在笔试范围之内,只是形式从“选答案”变成了“让你直接说”。如果你在笔试复盘时已经把原理搞明白了,这次串场就能很顺理成章地表达出来。比如你知道了TCP三次握手是为了“同步序列号和防止失效连接请求”,面试时说出来的内容就会比“因为所以要三次”显得有深度得多。
最后,复盘时还有一个容易忽略的动作:动手重写自己笔试时写得不好或者没写出来的编程题。不要只看答案,一定要亲手在编辑器里把它写出来、提交通过。因为看懂了和你自己能写出来之间,有一道巨大的鸿沟,笔试练的就是跨过这道鸿沟的能力。
5.4 心态管理:笔试不过不是能力问题,是准备问题
很多人一到在线笔试就紧张,尤其是倒计时显示“剩余时间10分钟”的那一刹那,脑子一片空白。这里分享一个我自己的解压小技巧:笔试开考前,用一分钟在草稿纸上写下“我能拿到的分数点”。比如“客观题我会的题有15道”“编程题第一题我会做”“即使不会的题我也能写出暴力解”——把这些“保底分数”写下来,心里的确定性会大幅提升。人的恐惧大多来自不确定性,当你把未知变成已知列表后,手就不会抖了。
还有一点,笔试没通过不代表你技术不行,更不代表你不行。2016年那场360笔试,我自己也没有走到最后一轮,但备考过程沉淀下来的C/C++基本功、网络知识体系和算法能力,在后来的工作里一直在受益。笔试只是求职这条路上的一个检查点,不是终点。把目光放在“借这场考试补足自己的知识短板”上,心态就会稳很多。
这些企业笔试题目虽然年代久了点,核心的考点和出题思路依然有很强的参考价值。我当时把这些题一道一道吃透的过程,其实就是一个系统整理自己计算机基础的过程。现在把这份复盘分享出来,希望准备笔试的你少走一点弯路,把时间花在真正重要的基础打磨上。