ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

搜狐2013校招研发笔试题解析:C++、算法与系统基础考点复盘

搜狐2013校招研发笔试题解析:C++、算法与系统基础考点复盘 翻移动硬盘时翻出一份搜狐2013校招研发工程师笔试题回忆版看到那些手写笔记很多画面一下子回来了。2013年正好是移动互联网起势的阶段Android开发、iOS开发这些岗位开始大量招人但门户、搜索、视频这些传统业务对后端研发的基本功要求一点没放松。那会儿的校招笔试不像现在很多公司先给你在线测评、再安排几轮电话面试更多是线下笔试一张卷子一支笔两个小时内完成。搜狐这套题我认为很有代表性既考C/C、操作系统、网络、数据库这些硬基础又考算法和数据结构的落地能力。今天把它整理出来结合我曾踩过的坑和事后复盘聊聊这类校招笔试到底考什么、为什么考、怎么准备。不管你是准备校招的在校生还是想系统梳理基础的后端开发者这篇内容应该都能用得上。1. 这套笔试题背后的时代背景与考察逻辑1.1 2013年前后互联网公司校招的特点2013年智能机出货量猛增各种App层出不穷很多互联网公司都在抢移动端产品和技术人才。但搜狐当时的产品矩阵还是比较传统的门户加视频加搜索后台服务对C、Java、Linux的依赖很重。研发工程师岗位招进去以后大概率要跟高并发服务、海量日志、分布式存储打交道所以公司并不指望应届生一上来就带项目而是希望这些人基础扎实、可塑性强。笔试就是第一道筛子把那些只会背题、不会思考的人挡在门外。那会儿的笔试题目风格和现在有区别现在的笔试很多在线上OJ里进行会重点考算法、动态规划、贪心甚至系统设计而2013年的线下笔试更“古典”一张试卷上既有选择题、填空题也有简答题和手写代码题内容覆盖C/C、操作系统、计算机网络、数据库和数据结构。题目普遍不算特别难但覆盖面很广需要你有系统的知识框架才能答完。这套题对后来准备面试的人有一个启发基础永远值得花时间。即使语言换成了Go、Python网络七层模型、进程模型、索引原理这些东西依然不变。我后来看新人的简历不少人写了各种框架但一问到底层协议或者内存分配就露馅。所以2013年的题放在今天依然是很好的自测清单。1.2 搜狐研发岗笔试的命题思路搜狐那套笔试题给我的第一印象是“务实”。它不像某些公司出一些脑筋急转弯式的怪题而是把一个开发者在日常开发中会遇到的真实问题包装成笔试题。比如C里问const放在不同位置的区别这个问题看起来基础但实际写代码时const用错了轻则编译不过重则埋下隐患。再比如Linux下如何查端口占用这几乎是每个后端开发每周都会使用的命令。命题基本围绕四个维度展开语言层C/C的语法细节、内存管理、指针、关键字系统层进程线程、内存布局、Linux常用命令数据层数据结构、经典算法、时间/空间复杂度分析网络层TCP/IP、HTTP、DNS这四个维度恰好构成一个研发工程师最基本的底座。四年后我坐在面试官位置上的时候发现面试应届生想考察的东西并没有变多少只是考察方式从笔试题变成了带项目深挖、线上问题排查模拟。所以当你觉得笔试题目太“八股”的时候不妨换个角度这些八股就是以后排查线上问题的底层语言。1.3 试卷结构长什么样印象中搜狐2013校招研发笔试卷整体分为四个部分选择、填空、简答和编程题。考试时间两个小时满分可能是一百或一百二具体分值记不太清楚了但题型结构很稳定。我做了一个大致结构表方便你对照回忆题型年份大致题量考察重点选择题2013约10-15题C语法、操作系统、网络填空题2013约5-8题程序输出、数据库SQL结果简答题2013约3-4题进程通信、TCP状态、索引优化编程题2013约2-3题链表、字符串、二叉树这类卷子的最大问题不是题难而是时间紧、知识点散。如果你平时没有系统梳理过很容易出现“每个题都见过但每个空都填不准确”的情况。我当年就是吃了这个亏后来专门把基础考点整理成知识树效果立刻不一样。2. 基础题型拆解C/C与操作系统的经典考点2.1 C/C高频题指针、内存、关键字C/C在研发笔试题里的占比通常最高。不是说公司有多爱C而是C的语法细节能直接暴露一个人对计算机底层原理的理解程度。指针、引用、内存布局、结构体对齐、static和const每个点都可以展开出不少题。先看一道经典的指针交换题#include stdio.h void swap(int *a, int *b) { int *tmp a; a b; b tmp; } int main() { int x 3, y 5; int *p x, *q y; swap(p, q); printf(%d %d\n, x, y); return 0; }问输出是什么答案是 3 5。很多人一眼扫过去以为swap交换了指针就会把x和y也换了其实没有。函数参数里的int *a和int *b本身是按值传递的指针变量函数体内只交换了这两个局部指针的指向并没有修改指针所指内存里的值。如果想交换x、y的值应该写成void swap(int *a, int *b) { int tmp *a; *a *b; *b tmp; }这类题考察的是“指针参数也是值传递”这件事。复习的时候可以画一张栈帧图把形参和实参的指向画清楚比硬记结论有用得多。再来看const的三种写法这也是那几年笔试题里的常客声明含义典型误用场景const char *pp指向的内容是常量不能通过p修改误以为p也不能改char *const pp本身是常量不能指向其他地址误以为内容不能改const char *const p内容和指针本身都是常量容易混淆区分方法很简单const修饰谁谁就不可变。const char *p这里const修饰的是char *p解引用后的那个char所以内容不可变char *const p里const修饰的是指针变量p所以p不能指向别处。记住“const修饰的是紧跟其后的类型”在大多数情况下都好用不过最准确的还是用“从右往左读”的方法。内存分区和结构体对齐也是高频考点。内存分区常考栈、堆、全局区、常量区、代码区比如字符串常量“hello”存放在常量区char *p hello再去修改p[0]会崩溃因为常量区只读。结构体对齐则要会算sizeof考虑到默认对齐规则。例如struct A { char a; int b; char c; };在32位平台默认4字节对齐下sizeof(struct A)等于12不是6。因为char a占1字节后面要补3字节对齐到4int b占4字节char c占1字节再补3字节总共12。如果把char放在最后同样12。但如果调整成员顺序为int b; char a; char c;则大小是8因为两个char可以共用一次对齐填充。这就是笔试里常出的“结构体对齐优化”问题。2.2 操作系统与Linux常见考察点操作系统常考的点基本固定进程与线程的区别、进程间通信方式、死锁的四个必要条件、虚拟内存、页面置换算法、并发与同步。2013年的笔试喜欢把进程通信放在简答题里让你列举几种方式并说明适用场景。我总结了一个表格通信方式特点适用场景管道半双工基于文件描述符父子进程之间数据量小消息队列内核维护有边界进程间小数据块传递共享内存读写快不需要内核拷贝大流量高频数据交换信号量用于同步不传数据保护共享资源套接字可用于不同主机网络通信信号异步通知简单事件通知笔试里经常给一个场景两个进程需要高频交换大量数据最合适的方式是什么答案通常是共享内存。因为管道和消息队列都需要通过内核缓冲区拷贝数据共享内存直接映射同一块物理内存省去了两次拷贝。但共享内存有个问题多进程同时写会乱必须配合信号量做同步。这个场景在后端开发里很常见比如多个worker进程共享一份统计数据。Linux命令也是常考的实操点。题目可能问“线上服务器CPU飙高你如何定位问题”答案一般涉及top查看进程、pidstat或ps -Lp看线程、perf采样或strace跟踪系统调用。2013年那时候perf用得还不普遍更多是top、vmstat、iostat、netstat这些。还有一道让我印象深刻的fork题#include stdio.h #include unistd.h int main() { fork(); fork(); printf(hello\n); return 0; }问终端会输出几行hello。答案是4行。第一次fork后1个进程变2个第二次fork后2个进程各自再fork一次变成4个。每个进程都会执行printf所以输出4行。如果输出重定向到文件因为缓冲区会被复制也可能输出4次在终端上printf遇到换行会立即刷新所以能看到4行。这个细节说明fork不仅复制代码还会复制进程的地址空间和缓冲区状态。3. 数据结构与算法笔试中的重头戏3.1 经典数据结构的考查方式数据结构题几乎是校招笔试的必选项原因很简单数据结构是算法落地的基础也是衡量一个人编程基本功的最好标尺。链表的增删改查、栈和队列的应用、二叉树遍历、堆排序这些内容都可能在30分钟内出现。链表题里最高频的是反转链表。题目通常会给你一个单链表头节点要求写出迭代或递归实现。我建议先写迭代版本因为代码简洁、不易栈溢出struct Node { int val; Node* next; }; Node* reverseList(Node* head) { Node *prev nullptr; Node *cur head; while (cur ! nullptr) { Node *next cur-next; cur-next prev; prev cur; cur next; } return prev; }这段代码里最关键的一行是Node *next cur-next;它必须在修改cur-next之前保存下一个节点否则链表就断了。写完后再检查两个边界空链表和只有一个节点的链表。空链表时循环不执行返回prev为nullptr单节点时循环执行一次返回原节点都正确。笔试时能主动写出边界判断印象分会高很多。另一个经典题是判断链表是否有环。用快慢指针fast每次走两步slow每次走一步。如果链表有环fast和slow最终会相遇如果无环fast会先走到空。为什么快慢指针能追上因为进入环之后相当于fast每次比slow多走一步在环上两个指针的距离逐渐缩短最终相遇。复杂度是O(n)空间O(1)。这种解法要理解不能只背。二叉树常考的层序遍历通常借助队列实现。要注意“层与层之间如何分隔”的问题很多题目要求按层输出这时候可以在每层开始时记录当前队列长度然后只弹出这么多节点避免把下一层混进来。排序算法也是笔试选择题常客。要记住各排序算法的时间复杂度和稳定性算法平均时间复杂度空间复杂度是否稳定冒泡排序O(n^2)O(1)稳定快速排序O(n log n)O(log n)不稳定归并排序O(n log n)O(n)稳定堆排序O(n log n)O(1)不稳定计数排序O(nk)O(k)稳定如果题目说“要求O(n log n)时间且稳定”那就选归并如果要求“原地排序”排除归并快速排序或堆排序都可以如果数据范围很小可以考虑计数排序但不常见。3.2 编程题思路从题目到代码的拆解法2013年搜狐笔试题里我记得有几道编程题比较典型字符串去重、两数之和、二叉树层序遍历。现在看起来简单但考场上很多人在边界条件和时间复杂度上翻车。先看字符串去重。最简单可靠的做法是用布尔数组#include string #include vector using namespace std; string removeDuplicates(const string s) { bool seen[256] {false}; string result; for (char c : s) { if (!seen[(unsigned char)c]) { seen[(unsigned char)c] true; result.push_back(c); } } return result; }这里有一个容易踩的坑字符可能是带符号的char直接用作数组下标可能越界最好转成unsigned char。这正是出题人想看到的细节。如果题目要求不使用额外空间那就需要考虑双指针原地覆盖但会丢失原本的字符顺序或者需要额外排序情况会复杂很多。笔试时除非明确要求否则用O(256)空间换时间是最稳妥的。再来看两数之和。题目给一个数组和一个目标值找两个数的下标。用哈希表可以把时间降到O(n)#include unordered_map #include vector using namespace std; vectorint twoSum(vectorint nums, int target) { unordered_mapint, int valueIndex; for (int i 0; i nums.size(); i) { int remain target - nums[i]; if (valueIndex.find(remain) ! valueIndex.end()) { return {valueIndex[remain], i}; } valueIndex[nums[i]] i; } return {-1, -1}; }注意一个细节当数组里有重复值时如果提前把所有值放进哈希表后放入的下标会覆盖先放入的可能出错。上面的写法是边遍历边放入先检查剩余值是否已经存在保证不会使用同一个元素两次。编程题的答题策略也很重要。我在笔试现场的习惯是先在代码区写注释列出核心思路再补代码。就算代码最后有bug阅卷老师也能看到思考过程得分不至于太低。还有不要一开始就追求完美先把能跑通的主逻辑写出来再回头补充边界条件。4. 计算机网络与数据库容易丢分的部分4.1 TCP/IP协议栈的高频考点网络部分如果认真准备过其实是最好拿分的因为考点非常固定TCP三次握手、四次挥手、TIME_WAIT、TCP/UDP区别、HTTP状态码、DNS解析过程。三次握手和四次挥手我不建议死背要理解状态变化。三次握手可以类比两个人打电话确认对方在听先发SYN对方回SYNACK你再回ACK。四次挥手是因为TCP是全双工通道两边都要单独关闭。挥手后发起关闭的一方会进入TIME_WAIT持续2MSL核心目的有两个一是保证最后一次ACK如果丢失可以在超时后重传二是让旧连接中的延迟报文在网络中消失避免污染新连接。笔试经常考一个问题为什么TIME_WAIT要等2MSL而不是更短因为网络上可能还残留着旧连接的报文如果端口被立即复用新连接可能收到旧报文导致数据混乱。2MSL足够让一个报文在网络中往返一次后被丢弃或超时。HTTP状态码也是高频考点。301和302的区别尤其经典301是永久重定向搜索引擎会更新链接权重302是临时重定向搜索可能保留原地址。实际场景里网站换域名用301活动页临时跳转用302。后端开发如果分不清这两个接口调用方就可能被错误缓存。503和502也常被问502是网关收到了无效响应503是服务暂时不可用比如过载或维护中。DNS解析过程可能与系统设计题结合起来。访问一个域名时先查浏览器缓存再查操作系统hosts文件接着查本地DNS服务器。如果本地DNS没有缓存它会代为发起逐级查询先问根域名服务器再问顶级域名服务器最后问权威域名服务器拿到A记录后返回并缓存。这个链路虽然表面像背诵但理解它能帮你排查域名解析慢、解析错乱的问题。4.2 数据库查询与索引优化数据库题在2013年研发笔试题中一般占10-20分不会太难但常见的几种SQL写法和索引原理一定要会。典型题目是“学生表、课程表、成绩表查询选修了全部课程的学生姓名。”标准写法是用双重NOT EXISTSSELECT s.name FROM student s WHERE NOT EXISTS ( SELECT 1 FROM course c WHERE NOT EXISTS ( SELECT 1 FROM score sc WHERE sc.student_id s.id AND sc.course_id c.id ) );这个解法的思路是不存在任何一门课程这个学生没选过。逻辑上等价于选修了全部课程。很多新手会想用COUNT(*)和总课数比较但需要处理成绩表里没有记录的课程容易漏。双NOT EXISTS的写法更通用也更接近集合逻辑。另一个典型是“查询每门课平均分大于80的课程名”。要注意WHERE和HAVING的区别SELECT course_id, AVG(score) AS avg_score FROM score GROUP BY course_id HAVING AVG(score) 80;WHERE不能使用聚合函数HAVING专门用来过滤分组后的聚合结果。这个点虽然基础但笔试时很容易被混淆。索引部分是简答题和选择题的常客。一个经典问法写了一个SQL查询很慢如何分析首先要执行EXPLAIN看执行计划确认是否走了全表扫描。如果没走索引检查查询条件是否使用了函数、隐式类型转换或like通配符在前这些都会导致索引失效。如果走了索引还是慢就要看是否出现了回表过多或者需要覆盖索引。一个容易被忽略的细节是最左前缀原则。比如联合索引(a,b,c)如果查询条件只用b而不用a就无法走这个索引。这在业务系统里非常常见很多人建了索引却用不上。笔试中可能给出一段SQL让你判断索引是否生效这些规则需要熟记。5. 完整答题策略与事后复盘5.1 时间分配与做题顺序拿到试卷后别急着下笔先花两三分钟把整张卷子扫一遍。我习惯按“简单编程题 - 选择题/填空题 - 简答题 - 困难编程题”的顺序做。因为编程题往往占分高而且需要进入状态放在后面容易心慌。把会的先拿到手再集中精力啃难点。我个人的时间分配大概是选择题和填空题40分钟简答题20分钟编程题50分钟剩下10分钟检查。这套时间在面对搜狐2013年这类卷子时还算合理。如果选择题里遇到拿不准的先标记不要在同一道题上扣太久。两道不会的题加起来可能已经占用了一道编程题的时间。5.2 我踩过的坑和总结的技巧当年笔试我丢了三个印象很深的分。第一是结构体对齐题我默认所有平台都是4字节对齐忽略了#pragma pack可以修改对齐数导致sizeof算错。第二是fork题目只算了第一次fork后的2个进程忘了第二次fork继续翻倍写成2而不是4。第三是SQL里HAVING和WHERE混用在WHERE后面写了AVG(score)被阅卷老师画了红圈。事后复盘我发现这些错误不是“不会”而是“不够细”。笔试考的就是你在紧张状态下能不能把最基础的点写对。后来我总结了一套复习方法把高频考点写在卡片上每个考点用一句话给出结论再配一个简单的例子。卡片不用多几十张即可考前翻一遍效果比临时抱佛脚好得多。答代码题的时候字迹和逻辑清晰比字多更重要。先在草稿纸上理清思路画出链表、指针关系再誊写到答题位置。很多阅卷老师会按步给分你的注释和边界判断都能帮你拿分。5.3 这套题对现在准备校招的人还有参考价值吗现在互联网公司的笔试形式变了很多都搬到了线上平台题型也增加了动态规划、深度优先搜索、系统设计等。但搜狐2013年这套题所覆盖的基础点依然是面试里绕不开的。C的const、static操作系统的进程通信TCP的TIME_WAITSQL的索引优化——这些知识在今天依然每天都会用。我和一些做校招面试的朋友聊过大家有一个共识应届生基础扎实比多会几个框架重要。框架可以进公司后快速学数据结构、操作系统、网络这些基础很难速成。所以如果你正在准备校招与其焦虑刷题数量不如先把这些基础考点一个个弄透。遇到一道不会的题不要只看答案要追问“它考的是哪块知识”“出题人想考察什么能力”。把一道题拆成知识点再补上对应的知识树这比刷一百道“已收藏”的题目有用得多。最后再分享一个小经验笔试结束后不管感觉如何我都会把没做出来的题抄一遍回去后对照资料重新做直到能独立写出完整答案。这个习惯让我在秋招过程中逐渐建立起了信心。搜狐2013那套题里的很多知识点后来我在实际开发排查问题时都遇到过。基础这个事没有捷径但只要你愿意花时间它一定会回报你。
返回列表