
这套2016年的搜狐研发工程师笔试题目放在2025年的今天来看表面上是一套“老古董”实际上我每次给准备校招的学弟学妹做模拟训练时都会把其中几道题翻出来。原因很简单它不像现在很多笔试那样偏、怪、堆砌冷门模型而是老老实实地考基本功链表、字符串、数组、动态规划、贪心每一题都在测试“你写代码的时候脑子是否清醒”。而且这几年大家刷题的语言渐渐从C转向Python2025年3月的Python等级考试里也出现了大量与这类经典笔试题同源的编程题。所以这篇文章我会把搜狐2016研发工程师编程题里比较有代表性的几类题目做一次深度拆解配上C和Python两种实现思路把题目背后的考察点、容易踩的坑、笔试时的答题顺序策略一次性讲明白。适合正在准备校招笔试、跳槽机试或者单纯想把算法基础打扎实的人阅读。1. 这套2016年的笔试题为什么现在还值得刷很多应届生一看题库写着“2016年”第一反应就是“太旧了不考了”。这个想法其实会害了你。老牌互联网公司的题库有一个特点核心题型非常稳定换汤不换药。搜狐这套2016研发工程师编程题覆盖的知识点恰好是后面十年笔试的高频区间与其去刷一堆乱七八糟的新题不如先把这套题的解题范式吃透。1.1 搜狐这套题的难度定位从公开流传的题库来看搜狐研发岗的笔试编程题通常是2到4道难度梯度非常明显第一题基本是模拟或字符串处理送分题第二题开始上排序、双指针、二分这类常规算法最后一道往往落到动态规划或者贪心用来区分“会写代码的人”和“真正懂算法的人”。这个梯度设计其实和现在大部分中大型公司的笔试是一致的。所以刷这套题的时候你应该抱着“模拟真实笔试环境”的心态去练而不是做一道看一道答案。1.2 从老题看大厂题型变化有人会问现在大厂笔试都爱考什么我的观察是基础数据结构题的比例在回升但提问方式更“工程化”了。比如以前直接问“反转链表”现在会包装成“实现一个任务调度器中等待队列的逆序输出”以前问“括号匹配”现在可能变成“校验某种配置文件的括号是否合法”。搜狐2016的编程题里就有不少这种“裸题”当时的裸题到现在变成了各种包装题的内核。如果你连裸题都写不利索面对包装题基本就是死路一条。反过来你把裸题的边界情况全部想清楚包装题只是多花点时间读懂题目而已。1.3 2025年用Python刷老题的现实意义结合2025年3月Python等级考试一级编程题的出题方向来看越来越多初学者开始用Python学算法。Python在笔试中的优势很明显代码量少、调试快、不容易因为指针和内存问题卡壳。但它的劣势也在这里——太多人用Python的时候不分析复杂度一上来就全用切片、集合、内置函数结果面试官一问时间复杂度就懵了。所以我的建议是用Python刷搜狐2016这套题时每道题都强制自己用“基础数据结构 显式逻辑”实现一遍尽量少用黑魔法。比如反转字符串别直接切片倒序就结束了老老实实双指针走一遍。这样面试时手撕代码才不会露怯。1.4 我选取的题目范围说明由于完整原题的流传版本很多我在本文中挑选的是多年来被反复讨论、也是最符合搜狐这套题风格的几类代表题型包括字符串翻转与括号匹配、数组中第K大元素、合并区间、滑动窗口最大值、动态规划与贪心经典模型。这些题目在公开题库中都能找到对应版本我会在每道题上标明考察点、完整思路、可用代码和易错点。你可以把它当成一份“高频题型复习地图”也可以当成一次模拟实战来连刷。2. 高频题型拆解字符串与模拟题的易错点字符串题在笔试里属于“看起来简单做起来一堆bug”的类型。搜狐这套题里的字符串题难度不高但非常考验基础是否扎实。我遇到过太多人在这类送分题上丢分有人是索引没理清有人是没考虑空串有人是没注意大小写。下面这两类题目基本可以代表搜狐2016题单中字符串题的考察风格。2.1 字符串翻转问题题目模型给定一个英文句子要求将句子中的单词顺序翻转但单词本身保持原来的字符顺序。比如输入“I am a coder”输出“coder a am I”。这是搜狐笔试题单里的经典题型后续很多公司都出了变体版有的要求去掉首尾空格有的要求单词间只保留一个空格有的要求标点符号跟着单词走。但我建议先把最基础的版本写对。思路很简单先整体反转整个字符串然后遍历字符串遇到空格或字符串结尾时反转当前单词。C版本#include iostream #include string #include algorithm using namespace std; string reverseWords(string s) { // 先反转整个字符串 reverse(s.begin(), s.end()); int n s.size(); int start 0; while (start n) { // 跳过空格 while (start n s[start] ) start; int end start; while (end n s[end] ! ) end; // 反转当前单词 reverse(s.begin() start, s.begin() end); start end; } return s; }Python版本def reverse_words(s: str) - str: chars list(s) n len(chars) def reverse(left: int, right: int) - None: while left right: chars[left], chars[right] chars[right], chars[left] left 1 right - 1 reverse(0, n - 1) i 0 while i n: while i n and chars[i] : i 1 j i while j n and chars[j] ! : j 1 reverse(i, j - 1) i j return .join(chars)这里最容易犯的错是整体反转之后单词内的字符顺序也是反的很多人就停在这一步直接返回了。笔试时这种错误特别冤因为自己本地测试时经常用对称单词“aba”这种根本测不出问题。建议专门用“I am a coder”这种不对称的用例来验证。2.2 括号匹配问题题目模型给定一个只包含左右小括号的字符串判断是否为合法括号序列。进阶版会扩展到中括号、大括号甚至要求返回第一个不匹配的位置。搜狐2016这类题目的考察核心是“栈”这个数据结构。左括号入栈右括号出栈并匹配。最后检查栈是否为空。Python版本def is_valid_brackets(s: str) - bool: stack [] pairs {): (, ]: [, }: {} for ch in s: if ch in ([{: stack.append(ch) else: if not stack or stack[-1] ! pairs[ch]: return False stack.pop() return not stackC版本#include iostream #include stack #include string using namespace std; bool isValid(string s) { stackchar st; for (char ch : s) { if (ch ( || ch [ || ch {) { st.push(ch); } else { if (st.empty()) return false; if (ch ) st.top() ! () return false; if (ch ] st.top() ! [) return false; if (ch } st.top() ! {) return false; st.pop(); } } return st.empty(); }这个题有两个高频错误点。第一个是忘记在遇到右括号时先检查栈是否为空如果栈空了还去取栈顶元素就会导致崩溃或者越界这属于笔试中的致命伤。第二个错误是在循环结束后忘记检查栈是否为空输入是“(()”这种左括号多余的情况就会被漏掉。2.3 模拟计算器表达式搜狐的题单里有不少“模拟题”比如实现一个只含加减乘除和括号的表达式求值。这种题不会出现特别复杂的优化但非常考察对栈和运算符优先级的理解。我建议的通用做法是使用两个栈一个存数字一个存运算符。遍历表达式时遇到数字就解析出完整数字入数字栈。遇到左括号直接入运算符栈。遇到右括号则一直弹出运算符计算直到遇到左括号。遇到运算符则先处理栈中优先级不低于当前运算符的运算符再入栈。最后清空运算符栈。这个逻辑背后的原理并不复杂。乘除优先级高于加减所以遇到加减时前面如果有乘除必须先把它们算完否则结果全乱。括号则将局部计算强行隔离优先处理。笔试时这种模拟题真不建议现场硬刚全功能版本。先把不带括号的加减乘除版本写出来再考虑括号一步一步加而不是一上来就试图写一个三百行的完整求值器。搜狐这类题目的判题用例通常不会卡得太严能够处理常见表达式就足够拿大部分分数。3. 排序与数组算法从暴力到优化的推进过程数组和排序相关的题目在搜狐2016研发工程师编程题里占比不小。这类题看起来“人人都会”但区分度恰恰体现在复杂度控制上。下面这三道代表题型我会从暴力解法讲起再一步步推到更优解法帮助大家建立一个完整的优化链条。3.1 数组中第K大元素题目模型给定一个无序整数数组找出其中第K大的元素。这道题是我在搜狐题单里见得最多的一类变体题现在各大公司的题库里依然非常活跃。最直观的解法是排序后按下标取时间复杂度是O(n log n)。笔试中只要K接近数组长度这个解法完全够用。但如果你想展示扎实的基本功可以用快速选择算法期望时间复杂度降到O(n)。核心思路借鉴快速排序的partition过程随机选择一个基准把数组分成大于基准和小于基准的两部分然后判断第K大的元素落在哪一部分只对那部分继续递归而不是像快排那样两侧都处理。Python版本import random def find_kth_largest(nums, k): target_index k - 1 left, right 0, len(nums) - 1 def partition(l, r): pivot_index random.randint(l, r) nums[pivot_index], nums[r] nums[r], nums[pivot_index] pivot nums[r] i l for j in range(l, r): if nums[j] pivot: nums[i], nums[j] nums[j], nums[i] i 1 nums[i], nums[r] nums[r], nums[i] return i while left right: pos partition(left, right) if pos target_index: return nums[pos] elif pos target_index: left pos 1 else: right pos - 1 return -1这里要注意第K大指的是从大到小排第K个位置。很多人在“大”和“小”上反了导致答案完全错误。笔试时如果时间紧张直接用排序解法是更稳妥的方案因为快选的随机性在极端情况下可能退化到O(n²)虽然概率低但笔试环境心态紧张时不要给自己找麻烦。3.2 合并区间问题题目模型给定若干区间把有重叠的区间合并。这道题在搜狐题单中出现的原因是它考察了两个点排序和边界处理。很多人想到了排序但排序规则没定清楚或者合并条件写错。正确的做法是先按区间起点升序排序然后遍历区间维护当前合并区间的起点和终点。如果当前区间的起点小于等于当前合并区间的终点说明有重叠更新终点为两者较大值否则把当前合并区间加入结果开始新的区间。C版本#include vector #include algorithm using namespace std; vectorvectorint merge(vectorvectorint intervals) { if (intervals.empty()) return {}; sort(intervals.begin(), intervals.end()); vectorvectorint res; int start intervals[0][0], end intervals[0][1]; for (int i 1; i intervals.size(); i) { if (intervals[i][0] end) { end max(end, intervals[i][1]); } else { res.push_back({start, end}); start intervals[i][0]; end intervals[i][1]; } } res.push_back({start, end}); return res; }这个题的坑在于区间边界“是否闭合”。如果题目说区间是闭区间起点等于上一个区间终点时就要合并如果区间是开区间则起点等于终点时不合并。搜狐这套题默认闭区间但有些变体的题目会特意强调边界条件务必仔细。3.3 滑动窗口最大值问题题目模型给定一个数组和一个窗口大小k窗口每次向右移动一格求每个窗口内的最大值。这道题的暴力解法非常容易想枚举每个窗口扫描窗口内的k个元素求最大值时间复杂度O(nk)。当n和k都很大的时候必超时。所以搜狐这类题目的主要考察点是单调队列。单调队列的核心思想队列中存的是数组下标并且保证从队头到队尾对应的数组值是单调递减的。这样队头永远是当前窗口的最大值。每次窗口移动时先去掉掉出窗口的队头下标再不断从队尾弹出值小于当前元素的元素然后把当前元素下标入队。Python版本from collections import deque def max_sliding_window(nums, k): dq deque() res [] for i, v in enumerate(nums): # 弹出窗口外的元素 if dq and dq[0] i - k: dq.popleft() # 从尾部弹出所有小于当前值的元素 while dq and nums[dq[-1]] v: dq.pop() dq.append(i) if i k - 1: res.append(nums[dq[0]]) return res这里有没有同学会问为什么从队尾弹出的是“小于等于”而不是“小于”其实用“小于”也不会错因为相等值的下标留在队里会导致过期判断更频繁但用“小于等于”会让队列更紧凑性能更好。笔试时这两种写法都能通过我习惯用“小于等于”。这种题在日常业务的“日志最近K条统计”或“流式数据滑动窗口计算”中都能找到对应场景属于典型的“代码不长但思路很值钱”的题目。4. 动态规划和贪心笔试中最容易翻车的两类题动态规划和贪心题在搜狐2016研发工程师编程题中属于拉开差距的部分。很多人的问题在于入门基础还行但一碰到需要自己推导状态转移方程的题就卡壳。本质上还是练得不够多。下面这两道题一个侧重状态设计一个侧重贪心策略证明恰好是两类题型的代表。4.1 最长上升子序列题目模型给定一个无序数组求最长严格上升子序列的长度。这里的“子序列”不是“子数组”不要求连续。这道题的经典动态规划解法是定义dp[i]表示以第i个元素结尾的最长上升子序列长度。状态转移方程是dp[i] max(dp[j] 1) 其中 j 从 0 到 i-1且 nums[j] nums[i]。初始时每个位置的dp[i]都至少为1因为单个元素本身就是一个上升子序列。最后答案取dp数组的最大值。C版本#include vector #include algorithm using namespace std; int lengthOfLIS(vectorint nums) { int n nums.size(); if (n 0) return 0; vectorint dp(n, 1); int ans 1; for (int i 1; i n; i) { for (int j 0; j i; j) { if (nums[j] nums[i]) { dp[i] max(dp[i], dp[j] 1); } } ans max(ans, dp[i]); } return ans; }这个版本的时间复杂度是O(n²)笔试中n在1000以内都没问题。如果n达到10的5次方量级就必须使用“贪心 二分”的优化版本用tail数组维护当前长度下的最小末尾值。我建议笔试中优先写O(n²)的DP解法因为逻辑清晰、不容易写错。只有明确看到数据范围很大时才去挑战优化版。很多人直接写优化版边界条件没理清反而扣分更多。4.2 股票买卖问题题目模型给定一个数组第i个元素表示第i天的股票价格允许最多完成一笔交易求最大利润。为什么说这类题容易翻车因为很多人会下意识选择贪心找到最低点然后找最高点。但“最低点”和“最高点”不一定能配对因为最高点可能出现在最低点之前。正确做法是动态规划思想下的滚动变量法用一个变量minPrice记录遍历过程中的最小价格用另一个变量maxProfit记录当前能获得的最大利润。每遍历一天尝试用当天价格减去minPrice更新maxProfit。Python版本def max_profit(prices): if not prices: return 0 min_price prices[0] max_profit 0 for price in prices: min_price min(min_price, price) max_profit max(max_profit, price - min_price) return max_profit这里需要注意什么注意题目说的是“最多完成一笔交易”如果改成“可以完成多笔交易”解法就完全变了变成贪心只要后一天价格比前一天高就累积利润。很多人在考场上没看清“一笔”还是“多笔”用了错误模型整道题垮掉。这是我在真实笔试图上见过最可惜的丢分原因。4.3 动态规划题目的通用解题套路既然聊到动态规划不妨把通用流程也整理一遍。我试过很多次只要按这个顺序走即便不是特别的DP高手也能稳定做出中等难度的DP题。第一步明确状态。问自己我关心哪些变量比如最长上升子序列关心“以某个位置结尾时的长度”股票问题关心“当前天数、手上是否有股票”。第二步写出转移方程。用文字描述“当前状态从哪里来”再转成代码。第三步确定初始化和遍历顺序。dp数组的初始值是什么从前往后还是从后往前第四步确认答案落在哪里。是dp数组的最后一个值还是dp数组的最大值还有一个细节DP写完之后一定要手动跑一个小用例在草稿纸上画一遍状态转移表。很多错误光看代码根本发现不了但一画表就原形毕露。5. 从真题到练法怎么利用老题备战招聘笔试这部分写给正在准备笔试的读者。刷题和实战其实是两码事很多人平时刷题六六六一到笔试就翻车原因往往不是能力问题而是答题策略出了问题。以下是我在多次模拟笔试和真实笔试中总结出来的经验。5.1 答题顺序先易后难别在第一题死磕搜狐这套题的难度分布很有代表性正式笔试时我强烈建议按照题目的难度评估来决定答题顺序而不是按照题目给出的顺序。先把所有题都看一眼找到最有把握的两道题先写。为什么因为笔试的评分通常按用例通过率给分偶尔出现一道和某道题完全没法下手的情况死磕不出来后面的送分题又没时间写这才是最亏的。我个人的策略是如果一道题超过25分钟还没有明确思路先标记起来去做别的题最后留时间回来写暴力解法能过多少用例算多少。5.2 用例设计笔试判分的背后逻辑笔试系统的判分逻辑是跑一组一组测试用例不是看了你的代码觉得“思路正确”就给满分。所以边界用例非常重要。很多人的代码在示例用例上跑得好好的一提交就挂绝大多数情况都是败在边界条件上。怎么练习边界用例设计每写完一道题强迫自己问几个问题输入为空的情况怎么处理输入只有一个元素的情况怎么处理数组长度为1窗口长度为1怎么处理全是相同元素怎么处理数组已经有序/逆序怎么处理存在负数或0怎么处理把这些情况都列出来再一一验证自己的代码。这个方法比盲目做十道新题都管用。5.3 代码规范别因为是笔试就随便乱写有些读者会觉得笔试只要答案对就可以了代码乱一点无所谓。这是很大的误区。笔试系统确实不看代码风格但你的代码是给自己看的如果你一边写一边重构变量名乱起缩进不对齐很容易在调试的时候把自己绕晕。我的建议是笔试时也用正常的代码规范写。变量名用有含义的英文名缩进保持一致关键逻辑旁边写注释。这不仅是给阅卷人看更是给自己留一条清爽的思路。我自己就吃过亏有一次笔试时间紧变量名直接a、b、c、d乱用结果提交前调试bug时根本分不清哪个变量是窗口左边界浪费了至少五分钟。5.4 刷题复盘把老题的价值榨干刷搜狐2016这类老题有一个其他题库替代不了的好处题目数量有限且每一道都有清楚的考察点特别适合做专题复盘。我的复盘方法是每刷完一套题花15分钟写一份简单的错题笔记记三件事这道题考的核心知识点是什么我第一次做的时候卡在了哪里下次遇到类似题型第一时间应该想到什么解法这份笔记不需要多华丽甚至不需要给别人看。但它能帮你把一套题从“我刷过了”变成“我完全吸收了”。很多人的刷题量很大但知识体系一团浆糊原因就是缺少了这一层反思。5.5 善用Python协助老题复习2025年3月Python等级考试一级的题目很多都把经典的数组、字符串操作包装成了“计算题”、“统计题”核心逻辑和搜狐2016的题目非常接近。这给我们的启发是用Python刷老题不必只追求刷完还可以把同一道题用不同的Python语法特性实现两三遍。比如反转字符串你可以用双指针写一次再用切片写一次。合并区间可以用传统循环写一次也可以尝试理解一下链式操作的写法。这样做的目的不是炫技而是让你对语言更熟笔试时不管用什么语言都能快速输出。写在最后的一点个人经验我自己从求职者变成面试官之后最大的感受是笔试题目从来不是为了难倒你而是为了在最短时间内看出你有没有扎实的代码功底。搜狐2016研发工程师编程题虽然年份看起来很久远但它考察的那些底层能力——逻辑清晰、边界齐全、复杂度有数、代码规范——到现在依然是拿到offer的通行证。如果你手头正好有几套这种老牌公司的历史真题别嫌它们旧。找个完整的时间段关掉手机通知设定好时间模拟一次真正的笔试。做完之后认真复盘把每个卡住的点都搞清楚。连续做三套以上你会明显感受到自己写题时的状态不一样了。笔试考的不只是知识更是你在有限时间下的稳定输出能力这种能力只能靠平时一次次模拟训练喂出来。