搜狐秋招笔试真题解析:数据结构与算法核心考点复盘 📅 发布时间:2026/8/29 6:37:31 👁 浏览次数: 说实话看到这份“搜狐2017秋招研发工程师笔试试卷一”的时候我第一反应是有点怀念。那几年正好是移动互联网最疯狂的扩张期搜狐、网易、腾讯这些老牌门户转型内容平台对研发岗的需求量非常大笔试题目也相对成体系。这份卷子虽然年份有点久但里面的考点放在今天看一点都不过时——数据结构、算法、网络、操作系统、数据库基本覆盖了研发岗笔试的“老三样”而且难度把握得比较均衡既有送分题也有区分度很高的压轴题。如果你是准备校招的在校生或者想跳槽但好几年没刷过题的老开发这套卷子都值得花两个半小时认认真真做一遍。我当年帮学弟学妹辅导笔试题的时候就经常拿这套题当模拟考因为它很能反映一家老牌互联网公司对“基础是否扎实”的判断标准。下面我按实际做题的顺序把整套卷子里几个核心模块逐一拆开讲清楚每道题背后的考点、解题思路以及我在实际批改和复盘时发现的高频错误。1. 试卷整体设计与考点分布1.1 搜狐秋招笔试的命题思路搜狐这样的公司出笔试题思路和 BAT 那种“海量投递、海量筛选”的模式不太一样。它不是纯粹要难倒你而是想在有限的两个小时里快速判断你有没有扎实的计算机基础以及有没有解决实际问题的代码能力。整套卷子大致分三个梯度。第一梯度是基础送分题集中在选择题的前半部分比如“TCP 三次握手的状态变化”“数据库事务的 ACID 特性”“栈和队列的区别”这类概念题基本是大学课本原话只要你认真上过课就能选对。第二梯度是中等题开始涉及一些简单的计算和推导比如给一个递归函数让你算时间复杂度或者给一段 SQL 让你判断执行结果这部分考察的是你能否把知识用起来。第三梯度是压轴题通常是最后两道编程题需要你现场设计算法并写出可运行的代码这部分才是真正拉开差距的地方。有意思的是这套卷子对“工程实践”的考察比重比一般公司要高。我印象很深的是里面有一道关于 Linux 文件权限的题还有一道关于 Git 操作结果的题。这种东西在学校里不会专门教但实际工作中天天用搜狐出这类题其实是在暗示招你进来不是让你做研究是让你能直接上手干活。1.2 核心考点覆盖与分值对比我统计了一下这套卷子的考点分布大致如下表所示考点模块题量占比典型题型难度系数数据结构与算法约35%链表操作、二叉树遍历、动态规划高计算机网络约20%TCP/UDP、HTTP 状态码、DNS 解析中操作系统约15%进程调度、死锁、内存管理中数据库约15%SQL 编写、索引原理、事务隔离级别中Linux/工程工具约10%文件权限、Git 操作、Shell 脚本低其他概率、逻辑约5%概率计算、逻辑推理低从这张表能看出来数据结构与算法是绝对的重头戏这和现在所有大厂的笔试风格是一致的。但搜狐特别的地方在于它的网络和操作系统题目占比明显高于一些新兴互联网公司这可能是因为搜狐的服务器端业务多对工程师的网络基础要求更高。所以如果你打算投这类传统门户转型的互联网公司计算机网络一定要好好复习尤其是 TCP 协议那一块几乎是必考。我建议你拿到一套笔试题时别直接就埋头做先花五分钟把题目整体扫一遍标出哪些是“稳拿分”的哪些是需要花时间算的哪些是可能需要放弃的。这套策略我在后面还会详细讲。2. 笔试核心题型拆解与解题策略2.1 数据结构链表、二叉树、栈与队列数据结构这块搜狐特别偏爱链表和二叉树基本每年必考。2017 年这道卷子里有一道链表题我印象很深给定一个单链表要求判断它是否有环如果有环找出环的入口节点。这题考的是快慢指针也就是 Floyd 判圈算法。思路不复杂用两个指针慢指针每次走一步快指针每次走两步如果链表有环两个指针一定会在环里相遇。但很多人做到这里就停了忘了题目还要求“找出环的入口”。找入口的关键是一个数学推导。假设链表头到环入口的距离是 a环入口到相遇点的距离是 b相遇点继续走到环入口的距离是 c那么环的长度就是 b c。慢指针走的距离是 a b快指针走的距离是 a b n(b c)其中 n 是快指针在环里绕的圈数。因为快指针走的距离是慢指针的两倍所以有2(a b) a b n(b c)化简之后得到 a (n - 1)(b c) c。这意味着从链表头到环入口的距离等于从相遇点继续走到环入口的距离再加上若干圈环长。所以当两个指针相遇后把一个指针移回链表头另一个保持在相遇点然后两个指针每次都走一步它们再次相遇的位置就是环入口。这个推导不算难但考场上能写出来的人不多因为很多人只记住了“快慢指针判断是否有环”没深究过“怎么找入口”。这也是我常跟学弟学妹说的刷题不能只背结论要把推导过程吃透否则题目一变就懵。二叉树这边搜狐考了一道中序遍历的非递归实现这题其实比递归实现更贴近工程场景因为递归有栈溢出的风险。标准做法是用一个显式的栈来模拟递归过程vectorint inorderTraversal(TreeNode* root) { vectorint result; stackTreeNode* stk; TreeNode* current root; while (current ! nullptr || !stk.empty()) { while (current ! nullptr) { stk.push(current); current current-left; } current stk.top(); stk.pop(); result.push_back(current-val); current current-right; } return result; }这段代码的核心逻辑是“先一路向左压栈弹栈时访问节点然后转向右子树”。很多人写错是因为在转向右子树之后忘记把当前节点置空导致死循环。这个细节我后面还会强调。栈和队列的基础题反而比较简单搜狐主要考它们的应用场景区别。比如“用两个栈实现一个队列”这道经典题思路是入队时往栈 A 压出队时如果栈 B 不为空直接弹栈 B如果栈 B 为空先把栈 A 的所有元素都弹出来压进栈 B再从栈 B 弹。这样做的摊还复杂度是 O(1)因为每个元素最多被移动两次。这道题能考察你对栈和队列本质特征的理解栈是先进后出队列是先进先出两个“后进先出”叠在一起通过两次反转就实现了“先进先出”。2.2 计算机网络TCP、HTTP 与 DNS 高频考点搜狐的网络题风格比较务实不怎么考晦涩的协议细节而是偏重“实际工作中你真的会用到的”。TCP 三次握手是必考的但搜狐喜欢变着花样考。比如给你一个客户端的状态序列问你每一步对应什么状态。我做了这么多年面试官发现很多候选人能背出“SYN_SENT、SYN_RCVD、ESTABLISHED”这些状态名但真让他描述“为什么二次握手不行”就卡住了。这个问题的核心是TCP 是双向通信的客户端和服务端各自需要确认对方的收发能力。三次握手实际上是在交换两个独立的“信道确认”第一次握手客户端发送 SYN服务端收到后确认了“客户端的发送能力”和“服务端的接收能力”都没问题。第二次握手服务端发送 SYN ACK客户端收到后确认了“服务端的发送能力”和“客户端的接收能力”都没问题。到这一步客户端已经确认了双方的收发能力但服务端还不知道“客户端的接收能力”是否正常所以还需要第三次握手客户端发送 ACK服务端收到后确认了“客户端的接收能力”正常。只有经过这三次双方才能在逻辑上确信“我说的话你能听到你说的话我也能听到”。HTTP 状态码那块搜狐考了一个非常实际的场景用户在浏览器里访问一个不存在的页面服务器返回什么状态码答案是 404。但题目喜欢绕个弯问你“如果这个页面在服务器端因为权限不足无法访问返回什么”答案是 403。很多人把 404 和 403 搞混。我的记忆方法很简单403 是“你有资格问但没资格看”404 是“你问的东西压根不存在”。另外搜狐还挺喜欢考 301 和 302 的区别。301 是永久重定向搜索引擎会把权重转移到新地址302 是临时重定向搜索引擎保留原地址的权重。这个在网站迁移和 SEO 优化中特别重要。DNS 解析过程也是一个高频考点搜狐喜欢让你描述“从输入 www.sohu.com 到页面加载DNS 经历了什么”。完整流程是先查浏览器缓存再查操作系统 hosts 文件然后查本地 DNS 服务器本地 DNS 服务器会先查自己的缓存没有的话就向根域名服务器发起迭代查询根域名服务器会告诉你“com 顶级域服务器的地址”再去问 com 顶级域服务器它会告诉你“sohu.com 权威服务器的地址”最后去问 sohu.com 的权威服务器拿到 www 这个主机的 IP 地址。这道题在改卷时我发现一个很有意思的现象大部分人都知道“递归查询”和“迭代查询”这两个名词但搞不清楚谁对谁用什么方式。其实记住一条就行主机到本地 DNS 服务器是递归查询本地 DNS 服务器到根/顶级/权威服务器是迭代查询。递归的含义是“你替我把事情办完最后给我结果”迭代的含义是“你给我指个路我自己去问下一家”。2.3 操作系统与数据库并发控制与索引优化操作系统这块搜狐最爱的考点是死锁的四个必要条件互斥条件、请求与保持条件、不可剥夺条件、循环等待条件。选择题通常给一个场景问你会不会产生死锁。比如“两个进程各自持有一个资源同时请求对方持有的资源”这就是典型的死锁场景。数据库方面2017 年这套卷子出了一道关于索引失效的题非常经典。题目大概是有一个表 user字段包括 id、name、age、phone索引建立在 (name, age) 这个联合索引上问以下哪些查询会走索引。答案涉及最左前缀原则。联合索引 (name, age) 相当于先按 name 排序再按 age 排序。所以查询条件里必须包含 name 字段才会走索引。如果只查 age索引就用不上。很多人没搞明白这个原理是因为不理解联合索引底层的 B 树结构——它其实是一棵按多个字段依次排序的树你先得确定第一层排序的字段才能继续在第二层查找。那年的压轴 SQL 题是求“每个部门工资最高的员工”。这个需求在实际工作中非常常见标准解法是用窗口函数SELECT department, employee, salary FROM ( SELECT department, employee, salary, RANK() OVER (PARTITION BY department ORDER BY salary DESC) AS rn FROM employee_salary ) t WHERE rn 1;如果你对窗口函数不熟也可以用传统的方式先找出每个部门的最高工资再关联原表SELECT e.department, e.employee, e.salary FROM employee_salary e INNER JOIN ( SELECT department, MAX(salary) AS max_salary FROM employee_salary GROUP BY department ) d ON e.department d.department AND e.salary d.max_salary;两种写法都能得到正确答案但如果部门里有两个人工资一样高第一种写法用 RANK() 会把两个人都查出来第二种写法也会查出来行为是一致的。但如果你用的是 ROW_NUMBER() 而不是 RANK()那就会只保留一个人这可能不是你想要的结果。这个细节在面试里很加分因为它体现你对“到底要取几条数据”这件事有清醒的认识。3. 编程题完整复盘从读题到 AC 的实战过程3.1 经典算法题原题还原与思路推演2017 年这套卷子的第一道编程题是“最长公共子序列”简称 LCS是一道无法回避的经典动态规划题。题目描述很直接给定两个字符串 text1 和 text2返回它们的最长公共子序列的长度。子序列不要求连续但必须保持相对顺序。一看到这种题你首先得判断出这是个 DP 问题。怎么判断我有个比较实用的经验如果题目里出现“最长”“最短”“有多少种”而且你感觉穷举会非常爆炸那大概率是 DP。DP 题的第一步不是想转移方程而是定义状态。LCS 的状态定义是教科书级别的dp[i][j] 表示 text1 的前 i 个字符和 text2 的前 j 个字符的最长公共子序列长度。有了定义转移方程就水到渠成了。当 text1[i-1] text2[j-1] 时说明这个字符可以成为公共子序列的一部分dp[i][j] dp[i-1][j-1] 1。当两个字符不相等时dp[i][j] max(dp[i-1][j], dp[i][j-1])意思是从两个方向“继承”较大的那个结果。我建议所有学 DP 的人都把这道题的推导过程自己在草稿纸上走一遍因为它是很多复杂 DP 题的基础模板。def longest_common_subsequence(text1: str, text2: str) - int: m, n len(text1), len(text2) # dp[i][j] 表示 text1 前 i 个字符和 text2 前 j 个字符的 LCS 长度 dp [[0] * (n 1) for _ in range(m 1)] for i in range(1, m 1): for j in range(1, n 1): if text1[i - 1] text2[j - 1]: dp[i][j] dp[i - 1][j - 1] 1 else: dp[i][j] max(dp[i - 1][j], dp[i][j - 1]) return dp[m][n]这段代码复杂度是 O(mn)无论 m 和 n 多大你都不可能用更优的渐进复杂度解决这个问题因为任何算法至少都要把两个字符串扫一遍。但搜狐这道题有个额外要求就是如果两个字符串的长度都超过 1000基础版本可能就会内存超限需要用滚动数组优化把二维数组压缩成一维。这个优化本质上是因为 dp[i][j] 只依赖 dp[i-1][j-1]、dp[i-1][j] 和 dp[i][j-1]也就是上一行和当前行的数据所以只需要保存两行就够了。能想到这一步在笔试里基本就是满分水平。3.2 第二道编程题Top K 问题的工程化变体这套卷子的第二道编程题更有意思它问的是给定一个很大的整数数组找出其中最大的 K 个数。这题如果数组很小直接排序然后取前 K 个就完了时间复杂度 O(n log n)。但它特意强调“很大”就是在暗示你不能全部排序。回答这道题有两条路线。第一条是维护一个大小为 K 的最小堆遍历数组如果当前元素比堆顶大就弹出堆顶把当前元素入堆。这样遍历完堆里存的就是最大的 K 个数时间复杂度 O(n log K)。当 K 比较小时这个方案非常高效。第二条路线是快速选择算法也就是快速排序的变种。每次选一个 pivot把数组分成大于 pivot 和小于 pivot 的两部分然后判断大于 pivot 的那部分数量如果刚好等于 K那这就是答案如果大于 K继续在大于 pivot 的部分里找如果小于 K那说明 pivot 本身和大于它的部分都是答案的一部分还需要在小于 pivot 的部分里再找剩下的。这个算法的平均时间复杂度是 O(n)是理论最优解。但我和很多面试官聊过大家一致认为第二道题真正的得分点不是算法本身而是边界条件的处理。比如当 K 等于 0 时直接返回空数组当 K 大于数组长度时应该返回整个数组还是报错数组里有重复元素时相同值怎么处理。这些细节在简洁的代码里很容易被忽略但恰恰是实际工程中必须考虑的问题。我通常建议写法是先把异常情况全部处理掉再写主干逻辑因为这样能让面试官一眼看出你“先想清楚再动手”的工程习惯。3.3 手写代码时的规范与细节在笔试中写代码和你在 IDE 里写代码是完全不同的体验。没有自动补全没有编译提示你只能在白板或在线编辑器里凭记忆把代码敲出来。我自己参加笔试和帮人批改卷子的经验是最影响得分的往往不是算法思路而是一些看起来很“低级”的错误。首要是函数签名。搜狐的在线笔试系统通常是自动判题它要求你实现的方法名、参数类型、返回值类型必须和题目给的模板严格一致。你哪怕逻辑写对了但函数签名对不上编译直接失败一分都没有。所以拿到编程题的第一步先把模板里的函数签名抄到答题区确保不跑偏。其次是缩进和括号。在线判题系统对空白字符的容忍度通常很高但人眼评分的时候缩进混乱的代码会非常减分面试官甚至可能怀疑你是不是真的会写代码。我有个习惯在纸上写代码时故意把每个大括号单独占一行左右对齐这样即使后来改动也能快速找到对应的括号。还有一个很容易被忽略的点是变量命名。笔试阅卷时面试官看你的代码第一眼看到的就是变量名。用 a、b、c 这种命名虽然也能跑但看起来就像未经思考的草稿用 left、right、current、maxHeap 这种命名即使代码有 bug面试官也更愿意相信你有能力修好它因为命名体现的是你大脑里的模型是否清晰。我建议在笔试前把常见数据结构的操作代码练到“肌肉记忆”的程度。比如单链表的反转、二叉树的前中后序遍历、二分查找、快排、归并排序、堆的插入和删除这些代码基本是每一场笔试都会出现的零件。你不需要临场想直接肌肉记忆写出来然后花更多时间去处理真正的难点。4. 备考秋招如何高效刷题与避坑4.1 应届生备战笔试的时间规划如果你现在是大三或研二准备参加下一年的秋招我建议你按 12 周来规划复习不要把战线拉太长也不要指望突击一个月就能搞定。前 4 周主攻数据结构与算法基础。把数组、链表、栈、队列、哈希表、二叉树、堆、图这 8 种基本数据结构从头到尾梳理一遍配合 LeetCode 上面“热题 HOT 100”中的简单题每天 3 道雷打不动。这阶段不要追求难题关键是建立“看到题目能判断出用什么数据结构”的本能。比如看到“维护前 K 大元素”就条件反射想到堆看到“配对/嵌套”就想到栈看到“索引进退”就想到队列。中间 4 周刷中等难度题开始接触动态规划、贪心、回溯、二分、滑动窗口这些常考算法范式。这个阶段我不建议按题号顺序刷而是按专题刷。今天专攻 DP明天专攻回溯刷完一个专题后停下来总结套路。比如 DP 题的套路是“定义状态 - 找转移方程 - 确定边界条件 - 优化空间复杂度”你总结多了就会发现大部分 DP 题都逃不出这几个步骤。最后 4 周进入模拟笔试冲刺阶段。每周挑两套往年的真题严格按照 2 小时的时限和真实的在线笔试环境来做。做完之后不要只看成绩要花至少 2 倍于考试的时间来复盘每道错题是因为知识点不会还是因为粗心还是因为时间分配不合理。我见过太多人刷了几百道题但真正模拟考试时还是栽在时间管理上前面选择题磨蹭太久后面编程题没时间写。4.2 做笔试题时的时间分配策略我自己比较推荐“三遍法”来应对笔试。第一遍花 5 到 10 分钟快速浏览所有题目标注出每道题的类型和难度。第二遍先做自己最有把握的题比如基础概念选择题和简单的编程题确保拿到保底分。第三遍再回头啃难题。选择题的时间分配要卡在 40% 以内。哪怕你看完题一点思路都没有也不要在一道选择题上纠结超过 3 分钟。因为选择题的答案是客观的蒙一个还有 25% 的正确率纠结到最后一题也未必能保证对反而挤占了编程题的时间。编程题千万不要上来就写代码。先在草稿纸上画出思路用哪些数据结构、大致的时间复杂度、边界条件是什么。我建议你在草稿纸上把算法思路写出来哪怕只是几个关键词远比直接上手敲代码效率高。因为直接敲代码很容易陷入“边写边改”的泥潭越改越乱。如果一道编程题 20 分钟还没有完整的思路果断放弃写暴力解。很多在线判题系统对暴力解的评分是“通过部分测试用例”“超时但有正确输出”也能拿到一定分数。宁可写一个时间复杂度过高但逻辑正确的暴力解也不要留白。留白是 0 分和部分分的天壤之别这个道理在职场上也一样。4.3 公司真题的深度复盘方法做真题的价值不在于题本身而在于通过真题摸清目标公司的出题偏好。我自己复盘真题时会做三件事。第一把公司近 3 年的笔试题按考点分类统计高频考点。比如搜狐的卷子连续三年都考了链表和二叉树TCP 状态变化几乎年年见。对这些高频考点投入加倍时间重点突破性价比很高。第二分析选项里埋的“坑”。出题人喜欢在干扰项里设置“半对半错”的选项比如“TCP 是面向连接的、可靠的、全双工的传输层协议但不保证传输顺序”——这个选项前面全对最后一句是错的因为 TCP 恰恰“保证传输顺序”。你如果对知识点的记忆是模糊的很容易被这种选项带走。通过复盘这些选项你能发现自己对哪些概念的理解是模棱两可的。第三把编程题的解法梳理成模板。比如“链表题套路”可以归纳为是否需要虚拟头节点、是否需要快慢指针、是否需要反转链表“子串类题目”得想想是用滑动窗口还是前缀和“树上路径题”大概率要 DFS 加回溯。你每刷一套真题就顺手把这些模板更新一遍到了真正笔试的时候看到题目先往模板上套能大幅缩短思考时间。5. 常见问题与避坑指南5.1 笔试中最容易失分的细节作为一个帮人批改过很多份笔试卷子的人我总结出下面这 5 个高频失分点几乎每一场笔试都会遇到。我把它做成一个速查表你可以对照着自查常见问题具体表现解决办法函数签名不匹配在线判题编译失败0 分先把模板函数签名抄到答题区再做改动边界条件遗漏空输入、K 0、长度不足等场景写代码前先枚举边界条件逐条处理堆栈内存超限没有考虑数据规模直接用 O(n²) 空间先看题目给出的数据范围再设计算法死循环链表题中节点没有及时后移在纸上模拟 3 轮循环确认指针移动顺序选择题过度纠结一道题耗 10 分钟编程题没时间写每道选择题限时 3 分钟超时先标记跳过除了这 5 个还有一个非常隐藏的扣分点代码注释。有些人喜欢写一堆注释这其实是好事但在笔试时要注意别写与题意无关的注释比如“这段代码是我想了很久才写出来的”这种不会加分反而会让面试官觉得你不自信。此外不要在代码里夹杂太多调试输出print 语句因为自动判题系统可能不会忽略这些输出而导致你的结果被判定为错误。5.2 这套卷子里最容易被翻车的争议题2017 年这套卷子有一道题在当年考生里争议很大给定一个包含 n 个数的数组找出数组中所有出现次数超过 n/3 的元素。这题如果不会 Boyer-Moore 投票算法变体很多人第一反应是用哈希表计数时间复杂度 O(n)、空间复杂度 O(n)。这在笔试中一般能得大部分分但题目如果加上了“尽可能降低空间复杂度”的条件你就得用摩尔投票法的推广版本。推广版本的思路是出现次数超过 n/3 的元素最多只有 2 个所以我们可以维护两个候选元素和两个计数器。遍历数组对于当前元素如果它等于候选一候选一计数加一否则如果等于候选二候选二计数加一否则如果候选一的计数为 0把当前元素设为候选一否则如果候选二的计数为 0把当前元素设为候选二否则候选一和候选二的计数同时减一。遍历结束后再遍历一遍数组统计两个候选元素实际出现的次数验证是否真的超过 n/3。这个算法的精妙之处在于它用“抵消”的思想代替了哈希表的额外空间是“空间换时间”思路的逆向版本。笔试时如果题目没明确要求用哈希表完全没有问题但如果题目问了“能否用 O(1) 空间”你写出摩尔投票法就是妥妥的加分项。这提醒我们刷题时同一个题目尝试多种解法并且理解它们之间的取舍关系面试时才会更从容。5.3 复盘工具与参考资料推荐刷笔试题这件事光靠自己埋头苦干效率很低我建议配合下面这些工具和资料来复盘都是我自己用下来比较靠谱的不存在广告纯分享。在线刷题平台推荐 LeetCode 和牛客网前者偏算法后者偏公司真题。如果你在备战秋招每天我会先花半小时在 LeetCode 上做 1 道中等题保持手感再花半小时在牛客网上刷目标公司的真题重点看讨论区里别人的题解思路尤其是那些“O(1) 空间”“双指针优化”的高赞回答。资料方面《剑指 Offer》是入门宝典题量不大但都是面试高频题《算法导论》是理论基石如果你时间充裕想深入理解算法原理可以看但应急刷题阶段可以放一放另外还有一本《程序员代码面试指南》是左程云写的里面的解题套路非常适合国内互联网公司的笔试风格。如果遇到一道题你怎么都想不明白可以试试“费曼学习法”把这道题的解法用自己的话讲给一个不懂编程的朋友听如果他能听懂你就真的会了如果讲着讲着自己卡住了说明你还有知识盲区需要回头看书。这个方法听着玄乎但我实测效果特别好因为很多知识你以为自己会了其实只是眼熟了。写在最后这套题告诉我们的三件事回到开头那份 2017 年的搜狐笔试试卷我认真做过、也认真讲解过即使过了这么多年我依然觉得它是一套质量很高的题。它没有故意刁难人但每一道题都在默默筛选“基础扎实 思维严谨 有工程意识”的人。所谓的“高分选手”往往不是天赋异禀的那种而是那些愿意把基础知识反复打磨、把每道错题彻底弄懂的人。如果你现在正处于备战校招的阶段我想分享一个我自己的体会不要太在意某一场笔试的得失。笔试不过是求职路上的一道门槛它检验的是你过去的积累而积累是可以靠每一天的刻意练习来改变的。今天做错一道题仔细弄懂它明天你就会在类似的题目上少花 5 分钟。日拱一卒功不唐捐把这套卷子拆透了你的信心也会跟着长起来。