
字节跳动的2024秋招算法岗笔试我前后参加了三场不同部门的笔试从第一次的“勉强做完”到后面几次“提前交卷且分数稳定”这个过程中踩过的坑和总结出的方法我觉得挺值得拿出来聊聊的。这篇文章不打算讲那些“刷题500题就能进大厂”的鸡汤而是老老实实拆一下字节算法岗笔试到底在考什么、怎么准备才对路、考场上怎么拿分。如果你正在准备2024秋招的算法岗或机器学习岗或者你已经在刷题但总觉得效率不高、一上笔试就发挥失常那这篇文章应该对你有用。我会把高频考点、刷题路线、考场策略和一些实操细节都展开讲尽量做到你看完能直接照着执行。1. 字节算法岗笔试全景它在筛选什么样的人1.1 笔试在整个秋招流程中的定位字节的秋招流程一般是网申 - 笔试 - 面试2-3轮技术面 1轮HR面- Offer。笔试卡在网申和面试之间属于硬筛选刷人比例不低。我身边不少同学简历很漂亮项目经历也不错结果笔试发挥失常连面试机会都没拿到非常可惜。字节的笔试并不指望你把所有题都ACAccepted全部通过它更像是一个分级筛选机制第一梯队的人全做完且全对第二梯队做完大部分第三梯队只能做出一两道。面试官看到的不仅是分数还有你的做题轨迹——比如你是先跳过了难题把简单题稳稳拿下还是死磕一道题导致后面的题没时间看这些细节其实都会影响印象分。所以笔试考察的不仅是算法能力还有时间分配、取舍决策、代码稳定性和边界意识。这些恰恰是工程师日常工作中最需要的基本素质。你可以在简历里写“精通各种算法”但笔试不会给你任何水分空间。1.2 题型结构、时间分配与判分逻辑我经历过的字节算法岗笔试大致的结构是这样的时长120分钟左右题量4道编程题偶尔会有选择题或简答题混进来但主流还是纯编程题难度梯度第一题是热身题基本是LeetCode中等偏下的难度第二、三题是中等偏上的核心题第四题通常是压轴题涉及状态压缩、线段树、高级图论等语言选择支持C、Java、Python、Go等主流语言选你最顺手的就行这里要特别说一下判分逻辑。字节笔试普遍采用“部分通过”机制也就是说每个测试点独立计分不是非对即错。哪怕你的代码只通过了30%的用例也能拿到这30%的分。很多同学一看到难题就放弃连暴力解法都不写这是非常亏的——暴力解法通常能拿20%-50%的分数堆起来也不少。再补充一个很多人忽略的点输入输出格式。字节笔试用的是类似牛客网的OJ模式不是LeetCode那种给你函数让你补全的写法。你需要自己从标准输入读数据、自己处理输出格式。我见过太多人平时在LeetCode上刷得飞起一上笔试连输入都不会写白白浪费大量时间。这个切换一定要提前训练。1.3 笔试分数如何影响后续面试字节的笔试成绩和面试并不是割裂的。据我了解笔试成绩会同步给面试官尤其是当面试官在“过与不过”之间犹豫时笔试成绩就是一个重要的参考信号。笔试做得好的候选人面试时会给面试官留下“基础扎实、代码能力过硬”的预设立场。反过来如果你笔试成绩一般但简历特别亮眼也有机会进面试——只是面试官可能更倾向在算法题环节多考你几道验证你的真实水平。所以我的建议是笔试尽量认真准备它能给你后面面试攒下不少“信用额度”。2. 高频考点拆解从基础数据结构到模型算法2.1 第一梯队动态规划、贪心、二分答案、双指针如果你只有两周准备时间请把重点压在下面这几个考点上。动态规划是字节笔试的绝对主力几乎每场都会出现而且往往不止一道。考察范围包括线性DP最长上升子序列、打家劫舍类、区间DP合并石子、括号得分、背包DP0-1背包、完全背包、树形DP、状态压缩DP。其中状态压缩DP通常在压轴题出现。建议把DP的基础模型全部刷熟尤其是状态定义和转移方程这两个环节——很多人DP做不出来不是不会写代码而是状态定义压根没想清楚。贪心算法喜欢和排序混在一起考典型场景包括区间调度、任务安排、分糖果、加油站等。这类题的难点在于证明贪心策略的正确性。笔试现场不需要写严格证明但要学会用“交换论证法”快速验证直觉。如果一个策略在交换任意两个元素的顺序后结果不会变差那大概率就是对的。二分答案的思路也很重要。特征是要求一个“最大值最小”或“最小值最大”的答案而且能写出来一个check函数来判断某个值是否可行。典型题目包括分割数组的最大值、小张刷题计划、运石头过河等。笔试时如果有题一眼看不出解法先想想能不能二分往往能打开突破口。双指针和滑动窗口在字符串和数组题里出场率极高。最长无重复子串、最小覆盖子串、三数之和、盛最多水的容器这些都是高频题。这类题通常不难但代码细节很多容易出现差一错误off-by-one建议提前把模板整理好。2.2 第二梯队图论、字符串、树与数据结构设计图论在字节笔试中通常作为中等题出现最常考的是最短路径Dijkstra、Floyd、拓扑排序、并查集。注意这里不会让你手写特别复杂的网络流最多用到带权并查集或者Dijkstra的堆优化版本。拓扑排序经常和生产场景结合比如任务依赖、课程表安排等。字符串算法里KMP的next数组、字典树Trie、字符串哈希都是值得准备的考点。这次很多热搜词里出现KMP和next数组相关的内容确实这个知识点在面试笔试里很常考。不过实战中字符串哈希往往比KMP更好写尤其是判断子串是否相等时配合二分还能做最长回文子串马拉车算法可以过但笔试时间紧哈希方案更省心。树的题目在笔试里一般不是重点但遍历、最近公共祖先、二叉树序列化与反序列化这类基础题还是要会。数据结构设计题偶尔会冒出来比如LRU缓存、LFU缓存、最小栈、单调栈/单调队列这些要能手写实现。2.3 容易被忽略的机器学习/深度学习算法基础注意我是算法岗不是纯后台开发岗所以笔试偶尔会涉及机器学习和深度学习的基础概念。比如KNN、K-Means、SVM、决策树、神经网络的反向传播原理等。这类题目通常以选择题或简单问答的形式出现不会让你手推完整的BP反向传播公式但你要能说出核心思路和适用场景。此外今年热词里出现不少粒子群算法、模拟退火算法、Dijkstra算法、PID算法等。这些“智能优化算法”一般不是笔试的重点但如果你投的是偏研究或偏特定业务的部门不排除会在加试或者面试里被问到。我的建议是把粒子群、模拟退火、遗传算法的核心流程记下来知道每一步在做什么就行不用深入实现。下面是我整理的考点优先级表格按照我的经验标注了重要程度考点模块优先级常见题型与出现频率动态规划极高线性DP/背包/区间DP/状态压缩几乎每场必有贪心高区间调度、任务分配常与排序结合二分答案高最大最小化、可行解判断双指针/滑动窗口高子串、连续数组、链表操作图论中高Dijkstra、拓扑排序、并查集字符串中KMP、字典树、字符串哈希树中遍历、LCA、序列化数据结构设计中LRU、单调栈、堆机器学习基础低概念题选择题为主智能优化算法低个别部门加试或面试出现3. 我的刷题路线与实战方法论3.1 三个阶段从基础巩固到模拟冲刺如果你从现在开始准备我建议按下面的节奏走整体周期6-8周比较稳妥。阶段一基础巩固2周左右。把LeetCode上的Top 100高频题刷一遍重点吃透数组、链表、树、哈希表、栈队列这些基础数据结构的操作。这一阶段不追求速度追求每一道题都理解清楚最好能手写复杂度分析。目标是把基本功打牢避免笔试时在简单题上卡壳。阶段二专题突破3-4周。按照上文整理的考点优先级一个专题一个专题地过。比如这周专攻动态规划下周专攻图论。每个专题刷25-30道题从简单到困难刷完做一个总结这类题的判断特征是什么、常用状态定义有哪些、转移方程怎么写。我之前整理过一份DP状态转移的模板笔记做题时直接查表套用效率提升非常明显。阶段三模拟冲刺2周。这个阶段要切换到OJ模式去牛客网参加模拟笔试严格按120分钟、4道题的标准来卡时间。模拟的目的不是“做完”而是训练“怎么在有限时间内拿到最多分”。你会发现自己经常陷入某个错误的思路里出不来这就是需要刻意练习的地方。3.2 核心能力复杂度分析、边界处理与代码规范笔试和平时刷题最大的区别是平时你只需要让代码在样例上跑通笔试则需要考虑各种极端情况。复杂度分析特别重要。很多同学写代码不过脑上来就O(n²)的暴力解数据一大就超时。这里有个经验法则数据规模在10^4以下可以接受O(n²)10^5以上必须O(n log n)或O(n)10^7以上基本只能O(n)或O(log n)。拿到题目先看数据范围再决定用什么算法这能帮你提前规避超时问题。边界处理是另一个拉开差距的地方。空数组、只有一个元素、所有元素相同、目标值在首尾、数值溢出……这些都是笔试用例喜欢埋的坑。我的习惯是写完代码后先把边界用例过一遍再提交不急着点运行。LeetCode上“通过率”极低但代码看起来没问题的题大概率就是死在边界上。代码规范同样会被无声地考察。变量命名清晰、逻辑分层明确、注释精准这些虽然不是判分点但好的代码风格会让你在自查时更快发现问题也会给面试官留下“代码素养不错”的印象。3.3 高频代码模板考前务必背熟笔试场上没有时间慢慢推导一些高频模板必须背到肌肉记忆。我把自己常用的几个模板列在下面你可以根据自己的语言习惯调整。二分查找模板Pythondef binary_search(nums, target): left, right 0, len(nums) - 1 while left right: mid left (right - left) // 2 if nums[mid] target: return mid elif nums[mid] target: left mid 1 else: right mid - 1 return -1并查集模板Pythonclass UnionFind: def __init__(self, n): self.parent list(range(n)) self.rank [0] * n def find(self, x): while self.parent[x] ! x: self.parent[x] self.parent[self.parent[x]] x self.parent[x] return x def union(self, x, y): rx, ry self.find(x), self.find(y) if rx ry: return if self.rank[rx] self.rank[ry]: self.parent[rx] ry elif self.rank[rx] self.rank[ry]: self.parent[ry] rx else: self.parent[ry] rx self.rank[rx] 1Dijkstra的堆优化版Pythonimport heapq def dijkstra(graph, start): n len(graph) dist [float(inf)] * n dist[start] 0 pq [(0, start)] while pq: d, u heapq.heappop(pq) if d dist[u]: continue for v, w in graph[u]: nd d w if nd dist[v]: dist[v] nd heapq.heappush(pq, (nd, v)) return dist记住模板的框架不等于会做题但模板能帮你省下大量基础代码的编写时间把精力留给核心逻辑。4. 考场上容易踩的坑与排查技巧实录4.1 读题不清和贪快笔试最大的坑真不是题难而是读题不清。字节的题目描述通常比较长喜欢包装一个业务场景真正的问题藏在最后几行。很多同学一看题目开头就兴奋觉得“这题我会”结果代码写到一半发现理解错了题意又得推翻重来。我的读题方法是先看输入输出样例再回去看题目描述然后用一句话在心里复述这道题要干什么。如果一句话说不清楚说明还没读懂不要急着写代码。笔试题型五花八门但绝大部分都能归约到某个经典模型上——字符串处理、区间计算、优化求解、图连通性等等读懂题后先想想这属于哪类再动手。4.2 边界条件与溢出问题边界的坑我已经强调过这里举一个实际的例子。有一道题是求“数组中最长连续递增子序列的长度”空数组应该返回0只有一个元素应该返回1。代码主体逻辑写对以后我就因为少写了一个空数组判断白白丢了一个测试点的分。溢出问题在C和Java中更突出Python不存在这个问题所以如果你用Python刷题这层风险自动消失了。但Python有另一个坑运行速度慢。复杂度过高的代码在C里可能压线通过在Python里就会超时。我建议能优化的地方尽量优化比如用数组代替字典、减少不必要的函数调用等。4.3 调试技巧与超时排查笔试环境通常没有断点调试功能最常用的调试手段就是print。很多同学一遇到问题就到处print这没问题但要控制时间。我给自己定的规则是如果连续print超过5分钟还查不出来立刻放弃这道题去做后面的题——等做完其他题再回来看往往一眼就能发现错误。超时问题排查有一个固定套路先检查复杂度看有没有多余的循环嵌套再检查数据结构看能不能用哈希表把查找从O(n)降到O(1)最后检查输入输出看是不是用了太慢的读写方式。我之前就遇到过一个题逻辑完全正确就是用了逐行input()读入大量数据导致超时换成sys.stdin.read()瞬间通过。这里整理了一个常见问题速查表笔试时可以直接对照排查现象可能原因解决方案部分用例超时算法复杂度过高换更低复杂度算法或用哈希优化部分用例答案错误边界条件没处理检查空输入、单元素、极端值运行报错段错误/越界数组越界或递归过深检查索引范围递归改循环或加memo同一用例时对时错全局变量污染检查多组测试数据间的状态重置输出格式不对多余空格/换行严格对照样例输出格式4.4 时间分配策略时间分配上我的经验是“前紧后松、先拿稳分”。第一题15分钟以内必须做完第二题和第三题各留40分钟第四题看剩余时间弹性处理。如果一道题想了15分钟还没有清晰思路立刻跳到下一题绝对不要恋战。还有一个小技巧做第二、第三题时如果想到了一个暴力解法先不要犹豫直接写出来提交把部分分拿到手。等全部题都过了一遍之后再回来优化。不要小看这部分分我有一场笔试就是因为第二题拿了暴力解的部分分总分比班里几个“死磕难题”的同学还高。5. 完整模拟题实战一道状态压缩DP题的思考过程5.1 题目与初步分析为了让你更直观地感受字节笔试的做题节奏我拿一道典型的压轴题来走一遍完整流程。这道题是我根据字节历年的出题风格模拟的不代表真实原题但思路和复杂度很接近。题目描述给定一个数组 nums 和一个整数 k判断是否能把这个数组分成 k 个和相等的子集。数组长度不超过16数组元素和不超过10000。看到“数组长度不超过16”第一反应就是状态压缩DP或者带剪枝的回溯。这道题如果按常规思路去搜k个子集的分配方式是指数级的所以必须用位运算压缩状态。5.2 状态定义与转移推导我们用二进制掩码表示数组中哪些元素已经被使用1表示已使用0表示未使用。例如n4时mask1011表示第0、1、3号元素已被使用。定义dp[mask]为“在mask状态下当前正在填充的子集的剩余容量”。如果某个子集已经填满剩余容量会重置为target每个子集的和。状态转移的思路选择一个尚未使用的元素i如果nums[i]不超过dp[mask]就把i加入当前子集得到新状态 mask | (1i)。转移后的剩余容量有两种情况如果剩余容量正好是0说明当前子集填满了dp[mask|(1i)]重置为target否则剩余容量为 dp[mask] - nums[i]。这里其实还有一个隐含的贪心优化先对数组排序优先放大的元素可以大幅减少搜索分支。这也是笔试里很实用的技巧。5.3 完整代码实现与复杂度分析def can_partition_k_subsets(nums, k): total sum(nums) if total % k ! 0: return False target total // k nums.sort(reverseTrue) if nums[0] target: return False n len(nums) dp [-1] * (1 n) dp[0] target for mask in range(1 n): if dp[mask] -1: continue for i in range(n): if mask (1 i): continue if dp[mask] nums[i]: new_mask mask | (1 i) new_remain dp[mask] - nums[i] if new_remain 0: new_remain target dp[new_mask] new_remain if new_mask (1 n) - 1: return True return False这段代码的时间复杂度是O(n * 2^n)n16时大约是100万次运算完全在可接受范围内。空间复杂度O(2^n)。这道题的演算过程想说明的是字节笔试的压轴题不一定需要你掌握多么冷门的算法关键在于能否根据数据范围推测出正确的算法方向然后把状态转移想清楚。如果能做到这一步哪怕编码过程中有些小问题调试起来也不会太费力。6. 一些心里话和实操建议写了这么多最后分享几点我个人的体会。第一刷题不要只追求数量要把每道做错的题都搞懂。我身边有人刷了400多题还是笔试挂掉也有人只刷了150题就顺利通过差别就在于是否真正搞懂了题目背后的模型。做完一道题花5分钟在笔记里记录一下这道题的特征是什么、用了什么算法、状态是怎么设计的、还能不能优化。坚持下来后期复习效率会非常高。第二笔试前一定要模拟真实环境。字节笔试用的OJ模式和LeetCode差别不小输入输出、判分策略、时间压力都不一样。我建议至少提前一周每天在牛客网上做一场模拟题把节奏感和心态都调整到位。模拟时要全程严格计时不能中途翻书这样才能测试出真实水平。第三字节的算法岗笔试虽然有一定难度但没有到“变态难”的地步。大部分题目还是以中等题为主压轴题虽然不好写但通过部分用例拿点分也是可能的。不要被网上“字节笔试太难”的帖子吓到静下心来准备你的实力会说明一切。最后再送大家一个小技巧考前把常用的输入输出模板、排序模板、二分模板、并查集模板整理在一页纸上笔试前花10分钟过一遍心里会踏实很多。这个习惯陪我走过了整个秋招也祝你拿到心仪的offer。