多彩编程 多彩编程MZPH · CODE BLOG
ARTICLE DETAIL

文章详情

深耕前端与后端开发技术的一线实战笔记与踩坑复盘。

搜狐秋招笔试复盘:算法、数据结构与C++核心考点全解析

搜狐秋招笔试复盘:算法、数据结构与C++核心考点全解析 先说结论这份“搜狐2017秋招研发工程师笔试试卷二”虽然已经过去一些年头但放在今天仍然是很有参考价值的一套题。我在准备秋招时刷过大量互联网公司历年真题搜狐这套卷子的风格非常典型——不堆偏题怪题重点考察基础功底、算法思维和工程落地能力和当前大厂笔试的出题思路基本一脉相承。如果你是正在备战秋招的应届生或者打算跳槽大厂技术岗的工程师我建议认真过一遍这套卷子并且不要只看答案而是顺着考点把相关知识点全部串起来。这篇博文我就以过来人的身份把这份试卷从题型结构、核心考点、编程题实战到避坑经验做一次完整复盘争取让你看完之后不只是会做某几道题而是对整个笔试准备方向有更清晰的把握。1. 试卷整体拆解搜狐秋招笔试题的出题思路与考点分布1.1 题型构成与分值分布从当年参加笔试的同学回忆版本来看这份试卷二的整体结构大致分为三类客观选择题、编程题、简答/设计题。客观题通常覆盖数据结构、算法、C/C语言、操作系统、计算机网络、数据库等基础模块题量在20到30道之间单题分值不高但覆盖面很广。编程题一般有2到3道分值占比最大是拉开差距的核心环节。简答或设计题则会考察你对某个技术方案的理解深度例如并发场景下的架构取舍、缓存一致性方案等。我当年拿到试卷后第一反应是这卷子出得挺“正”。没有那种为了难而难的脑筋急转弯也没有特别偏门的冷知识大部分考点都是科班课程里反复强调过的东西。但正因为基础反而很考验熟练度——选择题限时完成编程题要求一次性写出能跑的代码没有太多犹豫和改错的时间。1.2 题型背后的出题逻辑为什么搜狐要这样设计试卷站在公司角度想其实很合理。研发工程师入职后要面对的真实场景是大量工程代码的阅读与维护、线上问题的快速排查、业务需求到技术方案的转换。这就决定了笔试必须筛选出两类能力——第一类是计算机基础的扎实程度第二类是写代码时思路是否清晰、边界是否周全。所以你看这套题的选择题部分很多选项都是“基础陷阱”。比如指针和引用的区别、进程和线程的适用场景、TCP和UDP在不同业务下的选择这些不是靠背面试题就能答对的而是要在日常学习和项目实践中真正形成判断力。编程题则更直接几乎每道题都能在LeetCode或《剑指Offer》里找到原型但你会发现把原型题变换一下条件、换一个包装很多人就做不对了这说明对算法的理解还停留在“背模板”而不是“掌握思路”。2. 高频考点深度复盘数据结构与算法是重头戏2.1 排序、查找与字符串处理最容易被忽视的送分题这套试卷中的选择题排序和查找相关题目出镜率非常高。比如说快速排序在平均情况和最坏情况下的时间复杂度、排序算法的稳定性问题、二分查找的边界处理。别觉得这些简单我在实际笔试里见过不少人在二分查找的while循环条件上翻车。用左闭右开区间[l, r)还是闭区间[l, r]直接决定mid的更新方式是mid l (r - l) / 2还是mid (l r) / 2以及是r mid还是r mid - 1。这些细节在IDE里调试时很容易发现但在笔试网页编辑器里一旦逻辑不清就会陷入死循环或者漏判边界。字符串处理同样是个高频模块。搜狐这类门户起家的公司对字符串、文本处理类题目一直有偏爱。这类题考察的核心不外乎指针的移动、哈希表的计数、双指针滑动窗口。比如判断两个字符串是否为变形词、找出字符串中第一个只出现一次的字符、最长无重复子串的长度这些都属于笔试“必会题”。建议你专门花一到两天集中把字符串类题目过一遍重点不是记住代码而是理解每一步为什么要移动指针、为什么哈希表的更新要放在那个位置。2.2 二叉树与图论考察递归思维的分水岭二叉树相关题目在编程题里出现的概率极高。搜狐这套卷子里涉及的二叉树考点包括层次遍历、最近公共祖先、根据前序中序重建二叉树等。其中层次遍历是一个经典中的经典很多扩展题都是基于它变形出来的。层次遍历本身不难核心就是用队列保存每层的节点然后在循环里通过记录当前队列长度来控制层与层之间的切分。这里有个小陷阱容易踩——如果不记录当前层的节点数直接用queue.size()作为循环条件结果会因为队列中同时包含下一层的节点而多输出一层。正确的做法是每次进入外层循环时先取int size queue.size()然后内层循环只处理size个节点这样就能保证每次处理完的恰好是完整的一层。这个细节我会在下文编程题实战里专门展开。图论部分在这套卷子里没有出特别深的题但拓扑排序、并查集这类基础概念出现过选择题。拓扑排序本质上是Kahn算法加上一个入度数组和队列理解它的应用场景比如任务调度、依赖解决后代码是相当标准的模板。并查集则要重视路径压缩和按秩合并两个优化笔试时如果只写朴素版本最坏情况下会退化到O(n)级别的查询碰上大数据量直接超时。2.3 动态规划与贪心区分“会做”和“能做对”的关键动态规划是互联网大厂笔试的绝对主力也是区分度最高的知识点。搜狐这套卷子里动态规划相关题目主要围绕经典模型展开——背包问题、最长公共子序列、编辑距离、最长递增子序列等。为什么爱考这些因为它们虽然经典但真能在45分钟内写对的人其实不多。以最长递增子序列为例最直白的思路是O(n^2)的双层循环dpdp[i]表示以第i个元素结尾的最长递增子序列长度转移时遍历j i若nums[j] nums[i]则dp[i] max(dp[i], dp[j] 1)。这个写法能过大部分题目但如果数据范围到10^5O(n^2)就会超时需要用到贪心二分的优化方法——维护一个tails数组tails[k]表示长度为k1的递增子序列的末尾元素最小值遍历时用二分查找找到第一个大于等于当前元素的位置并替换。这里我建议你两种方法都手写一遍因为很多题目表面是动规实际用贪心二分才是最优解只背一种容易在变种题上吃亏。贪心算法的题在这套卷子里相对温和常见的就是区间调度类问题。区间选点、会议室预订、无重叠区间思路都是按结束时间排序再贪心选择。注意一点贪心题的正确性一定要能自证不能凭感觉写。我自己的习惯是在草稿纸上先画几个测试用例包括重叠情况、包含情况、边界情况跑一遍验证贪心策略确实能得到最优解再开始写代码。3. 语言与基础原理C/C、操作系统与网络的必考题3.1 C/C核心考点指针、内存与面向对象搜狐的研发岗笔试C/C相关题目占比不低。选择题里反复出现的就是指针相关的概念辨析指针数组和数组指针、函数指针和回调函数、const修饰指针的不同位置的含义。我建议你把下面这四个声明认真区分清楚const int *p; // p指向的内容不可变p本身可以变 int * const p; // p本身不可变p指向的内容可以变 const int * const p; // 两者都不可变 int const *p; // 等价于const int *p特别是第三个容易在选择题里作为干扰项出现。不要死记而是从右往左读声明——p先和const结合还是先和*结合决定了不可变的对象是谁。另外这块还常考内存管理比如堆和栈的区别、malloc/free和new/delete的底层区别、内存泄漏的常见场景。搜狐这类公司对工程能力要求比较高所以问到的点往往很实际。比如“程序运行一段时间后内存持续上涨”这就是典型的内存泄漏排查场景。你除了要知道valgrind这类工具的使用还要能说出常见泄漏原因——忘记释放、容器里存了指针但没在销毁时delete、循环引用导致智能指针失效等。3.2 操作系统与计算机网络常考细节汇总操作系统方面这套卷子的考点可以汇总成一张表来梳理。进程和线程的区别、进程间通信方式、死锁的四个必要条件、虚拟内存和分页机制这些都属于必背基础。搜狐这类偏应用型的公司不会问得太底层比如不会直接让你写一个页面置换算法但会在选择题里用场景化描述来考察概念辨析例如“多个线程同时写同一个全局变量导致结果不符合预期最可能的原因是什么”答案指向竞态条件和缺少同步机制。计算机网络更是笔试重灾区。TCP三次握手和四次挥手的过程细节、TIME_WAIT状态的作用、TCP和UDP的区别及适用场景、HTTP状态码的含义。这里我提醒一个高频考点在浏览器输入URL后发生的全过程。这道题几乎每年都会出现在各厂笔试或面试里它串起了DNS解析、TCP连接、HTTP请求、CDN命中、服务端处理、响应返回、浏览器渲染覆盖了网络、操作系统、Web开发多个层面。建议你自己动手写一篇完整的流程描述写的时候注意每个环节涉及的协议和端口号写完后放两天再回来看你会发现遗漏了不少细节。数据库这块则重点考察索引原理、事务的ACID特性、隔离级别和MVCC。搜狐的笔试选择题里出现过“索引为什么能加快查询速度”这种基础题本质上考察的是B树的结构特点——多路平衡查找树、叶子节点有序且用链表连接适合范围查询。这个知识点我建议你画一遍B树结构把聚簇索引和非聚簇索引的区别搞清楚。4. 编程题实战从读题到AC的全流程思路4.1 字符串处理类题目以循环移位包含判断为例编程题的第一道往往难度适中目的是让大部分人能进入状态。搜狐这类公司喜欢出字符串题这里我以一道常见的“字符串循环移位包含”题为例完整演示一遍我的解题流程。题目大意是给定两个字符串A和B判断A是否包含B的任意循环移位。也就是说如果B通过循环移位能得到B‘且B’是A的子串则返回true。第一步不是写代码而是理解问题本质。B的循环移位其实等价于将B与自身拼接成BB后在其中寻找长度为len(B)的连续子串。于是原题就转化为判断“BB中是否包含某个长度为len(B)的窗口该窗口是A的子串”。更进一步的简化是B的所有循环移位都会出现在BB里所以只需要判断BB是否包含在AA中或者反过来判断A中是否存在一个子串等于B的某个循环移位。比较清晰的实现思路是用滑动窗口或字符串匹配。#include iostream #include string using namespace std; bool isCircularContains(string A, string B) { if (A.empty() || B.empty()) return false; if (A.length() B.length()) return false; string BB B B; for (int i 0; i B.length(); i) { string cur BB.substr(i, B.length()); if (A.find(cur) ! string::npos) return true; } return false; }这段代码的核心是循环移位产生的所有字符串都在BB中以某个起点出现。所以只需要枚举起点i从i截取长度为len(B)的子串再去A里查找。如果数据量小A.find就够了如果A和B都很长建议改用KMP算法做单次匹配整体复杂度降到O(nm)。这道题考的不是你会不会调库而是能否把“循环移位”这个抽象概念转化成“拼接字符串”的具体操作。4.2 二叉树层次遍历的现场实现第二道编程题往往就开始区分水平了。以层次遍历为例我给你一个笔试时比较稳妥的写法。前面说过核心在于内层循环开始前记录当前层的节点数。#include iostream #include queue #include vector using namespace std; struct TreeNode { int val; TreeNode *left; TreeNode *right; TreeNode(int x) : val(x), left(NULL), right(NULL) {} }; vectorvectorint levelOrder(TreeNode* root) { vectorvectorint res; if (!root) return res; queueTreeNode* q; q.push(root); while (!q.empty()) { int size q.size(); vectorint level; for (int i 0; i size; i) { TreeNode* node q.front(); q.pop(); level.push_back(node-val); if (node-left) q.push(node-left); if (node-right) q.push(node-right); } res.push_back(level); } return res; }注意这里的int size q.size()必须放在内层循环之前。如果直接写for (int i 0; i q.size(); i)每次q.size()都会变化因为你在循环里同时做push和pop队列长度一直在改变结果就会错乱。这种细节在本地IDE里很容易调试但在笔试网页上有时很难一眼看出来建议平时就养成先存尺寸再循环的习惯。层次遍历的变种很多锯齿形遍历奇数层反转、从底部向上遍历最后reverse整个res、按层输出的每一行求最大值、右视图每层最后一个节点。你可以把层次遍历当作母题把常见变种都练一遍这样一道题的价值顶十道题。4.3 编程题的边界条件与复杂度控制编程题最恶心的地方其实不是思路而是边界条件。我统计过自己历年笔试的失误大概有三分之一是栽在空数组、负值、最大值溢出、单个元素等情况上。搜狐这套卷子的编程题也一样基本每一道都可以用特殊输入卡死粗心的人。拿数组相关的题目来说我在笔试时习惯先问自己几个问题输入数组为空怎么办数组长度为1怎么办数组元素有重复怎么办目标值不在数组中怎么办这些判断要在写核心逻辑之前就想清楚而不是等代码写完了再去补。另外要特别注意整数溢出尤其是涉及累加和乘法时该用long long就果断用long long不要因为看着“应该能过”就放一个int上去笔试数据范围往往比你想象的大。在线笔试平台的输入输出也是一个想不到的短板。很多人用惯了IDE里的模板到了笔试网页上连多组输入都不会处理。标准的做法是// 多组输入每组包含一个整数n和n个元素 int n; while (cin n) { vectorint nums(n); for (int i 0; i n; i) cin nums[i]; // 处理... }while(cin n)这种写法在本地IDE里可能不会触发但在OJ上就是标准写法。还有格式化输出保留两位小数、每行输出末尾不要多余空格这些细节一旦错了哪怕算法完全正确也会被判0分非常可惜。5. 那些年踩过的坑笔试高频拦路虎与避坑指南5.1 选择题里常见的“陷阱选项”梳理根据我对这份试卷和其他互联网公司笔试的观察选择题的干扰项设计通常有几类固定的套路。第一类是“概念偷换”比如把进程和线程的定义混在一起前面半句是对的、后半句是错的第二类是“范围扩大”比如“所有排序算法的最坏时间复杂度都是O(n^2)”出现“所有”“一定”“必须”这类绝对化表述的选项基本都是错的第三类是“单位混淆”比如传输速率用Mb/s还是MB/s给答案时数值不变但单位变了看着都对但其实差了8倍。我建议你在复习时建立一份“错题台账”不用记完整题目只要记录考点和踩法。比如考点常见干扰方式我的正确理解二分查找边界条件写错导致死循环使用左闭右开区间rmid时注意终止条件快速排序平均复杂度O(nlogn)被误认为稳定排序快排不稳定因为交换可能改变相等元素相对顺序TCP四次挥手TIME_WAIT状态误认为是主动关闭方主动关闭方最后进入TIME_WAIT等待2MSL虚函数构造函数中调用虚函数会多态构造期间不会多态调用的是当前类的版本事务隔离级别可重复读和幻读混淆可重复读只保证自己读的数据一致不阻止幻读并查集未做路径压缩导致超时路径压缩将树高降到近似常数复杂度接近O(1)这个表格可以根据你实际刷题不断扩充考前翻一遍比再做一套新题更有用。5.2 在线笔试环境下的实战避坑很多同学平时刷题用本地IDE顺风顺水到了在线笔试却各种翻车。我总结几个高频问题全部是亲身经历或身边同学踩过的坑。第一编辑器的自动补全和语法高亮可能和你用的IDE不一样。笔试平台通常只提供基础的代码编辑功能没有智能提示这意味着你必须能默写常用头文件和方法签名。比如C里要用unordered_map你得自己写#include unordered_map而不是依赖IDE补全。建议考前一周开始每隔一天就完全关掉补全在平台模拟环境上做一道题练出手感。第二本地能跑但提交后爆内存或超时。这是因为本地数据量小隐藏测试用例数据量大。遇到这种情况优先考虑是不是复杂度不够优比如O(n^2)是否能优化到O(nlogn)或O(n)其次检查是否有不必要的值拷贝函数传参时用vector 而不是vector 能省下大量时间和内存再次确认没有死循环。我曾经有一次笔试本地跑样例3秒就出结果提交后一直不结束最后发现是while (left right)这个等号写错了导致无限循环。第三题目没读完就想当然。编程题有时会在末尾给一个附加条件比如“如果输入包含多组数据需要循环处理”有人只处理了一组就交卷。务必养成读题时先看输入输出格式再开始设计算法的习惯。我自己的做法是读完题目先写注释把输入、输出、约束条件、示例都抄一遍在代码顶部相当于给自己画一张流程图这样写代码的时候不会跑偏。5.3 时间分配45分钟编程题的真实战场互联网公司笔试的编程题通常有2到3道总时间在60到90分钟。这意味着每道题留给你思考加写码的时间其实只有20到30分钟。很多同学在第一道题上死磕太久后面简单的题反而没有时间做非常亏。我建议的时间分配策略是拿到试卷先花3到5分钟快速浏览所有题目把题目分成“能马上做”“需要想一下”“暂时没思路”三档。先把“能马上做”的题目拿下拿到基础分再回头做“需要想一下”的最后才考虑“暂时没思路”的。千万不要按照题目顺序死磕。另外如果一道题想了15分钟还没有思路果断跳过先把其他题做完回头再想时往往会有新的灵感。6. 从一份试卷到一套知识体系备考延伸建议6.1 按知识模块刷题而不是按题库顺序刷很多人刷题有一个习惯打开LeetCode从头刷到尾。这个方式效率极低因为你今天做链表、明天做动态规划知识点没有形成体系前学后忘。我自己的经验是准备笔试要“按模块集中突破”。比如这一周只刷二叉树从遍历到各种DFS/BFS从普通二叉树到二叉搜索树把相关题全部过一遍做不出来就翻题解等这一周结束你会发现二叉树类的题基本都形成了一个模板感。针对搜狐这套卷子我建议按这个顺序安排模块数组与字符串 → 链表 → 栈与队列 → 二叉树 → 排序与搜索 → 动态规划 → 图论 → 贪心。每个模块花3到4天每天3到5道题重点记录错题。大概一个月左右就能把核心模块过完之后再做整套模拟卷针对性会强很多。6.2 把笔记变成你的“外脑”笔试准备过程中最值得积累的是一份“算法笔记”。不是那些网上的大而全教程而是你自己踩坑后总结出来的要点。举个例子我在笔记里记过这么一条二分查找的跳出条件如果区间是[l, r]闭区间那么循环条件应为while (l r)更新为l mid 1或r mid - 1如果区间是[l, r)左闭右开循环条件为while (l r)更新为l mid 1或r mid。每次写二分题前先看一遍这条笔记基本不会再错。类似的笔记还可以包括快排的partition写法常用两种Lomuto和Hoare至少要熟练掌握一种、拓扑排序的入度表构建、Dijkstra算法的优先队列优化写法等。这套笔记不是给别人看的你完全可以按自己舒服的方式来写。关键是它能在考前半小时快速帮你把知识点全部过一遍比临时翻书强得多。6.3 笔试之外从刷题到面试的无缝过渡最后想多说一句笔试和面试不是割裂的。搜狐这类公司面试时很可能会追问笔试里你做过的编程题“你当时为什么选择这个方案”“有没有考虑过另一个数据结构的解法”如果你笔试时只是搜到了答案或者背了模板这种追问很容易暴露。相反如果你是自己一步步推导出来的哪怕笔试分数不是最高面试官对你的印象也会非常好。所以我建议做编程题时养成一个习惯每道题做完了用白话说一遍你的思路——为什么选择这个数据结构、时间复杂度的瓶颈在哪儿、有没有可能优化。可以在笔记本上写也可以对着空气讲这个“费曼学习法”式的梳理过程能把一道题的收获最大化同时为面试口头表达提前做了准备。我自己当年就是靠着这套方法从笔试前简历上一片空白到面试时能对算法思路侃侃而谈。刷题不是目的建立解决问题的思维方式才是。如果你能把这份卷子里涉及的考点全部串成体系那你的秋招笔试基本就稳了。
返回列表