前阵子一个准备秋招的学弟拿着一张“LeetCode 热门 100 题”清单问我是不是全刷完再去投字节客户端开发更稳。我扫了一眼直接让他别按这套打。热门 100 覆盖面确实广但客户端开发岗的算法面有它自己的口味——它更偏爱能聊出工程直觉的题目而不是单纯拼刷题量。后来我在好几个校招、社招准备群里翻来覆去看到的高频关键词也印证了这一点字节 算法题、LC5 最长回文子串、LC073 爱吃香蕉的狒狒、LC494 目标和、leetcode 热门100题……大家搜索的聚焦点非常集中。这里先把问题挑明字节客户端开发岗的算法现场通常在每轮技术面中穿插 1~2 道代码题不搞像周赛一样的大题量。题目难度以中等为主偶尔会有 hard但真正卡人的地方多数不在“会不会解”而在“写得干不干净”和“能不能把思路讲明白”。这篇文章整理了面向字节客户端方向的高频题 12 道不仅给题号还会解释它们为什么高频、每道题该从哪个角度切入、最容易踩的边界坑在哪儿以及面试官大概率会在哪一层追问。适合正在准备字节客户端开发岗或者想把算法复习聚焦到客户端场景的读者哪怕你目标是后端第 3~5 部分的解题策略也能直接复用。1. 算法面之前先认清客户端岗位的题目偏好1.1 客户端开发岗的代码面和你想象的刷题场景不太一样我没办法替你保证每一轮面试的公式化流程但从这两年公开面经和接触过的候选人反馈来看字节客户端开发整体是“技术面 交叉/业务面 HR 面”的走向。算法题不是单独拎出来的一轮笔试而是以“手撕代码”的形式穿插在技术面中。一面往往先聊项目、问一些操作系统和网络基础中间突然抛一道代码题二面在深挖 Android/iOS 底层原理的同时也可能再带一道到了主管面算法题虽然少但更偏开放场景用来考察你在真实业务里有没有算法意识。为什么强调这一点因为很多备考者把 LeetCode 当成“期末考试”复习每道题都要背最优解却忽略了面试官更想看的是你面对一个没有标准答案的问题时怎么拆解边界、怎么控制代码结构、怎么把时间空间复杂度讲清楚。字节客户端的几轮算法环节本质上像一场结对编程讨论而不是在线判题系统。面试官坐在你对面能看到你写每一行代码前的停顿也能在你卡住时抛提示。你越早适应这种“边说边想”的节奏现场表现越稳。1.2 通用题单和客户端岗的适配度并没有想象中那么高热门 100 题当然要刷但我更建议把它当成“搜索引擎”而不是“必须从头精读的书”。覆盖面宽的代价是题型多、难度波动大并且不是每道题在客户端岗都有较高上镜率。举个例子堆/优先队列相关题目如合并 K 个升序链表虽然核心但客户端代码面出现频率并不算很高反而是树遍历、字符串滑窗、LRU 缓存这类题几乎盘踞在热词榜上。你的备考时间有限应该按“最高性价比”排序。我圈这 12 道题时用了三个标准。第一在面向字节客户端方向的面经讨论里出现过多次关键词指向清晰第二它能被追问不是背完就结束而是有边界条件、有不同解法权衡第三它最好能映射到客户端实际工程比如图片缓存、View 树遍历、日志区间合并。有了这套标准最终选出的题才不会变成拍脑袋的题目堆砌。2. 12 道高频题速览它不只是一串题号更是一张能力映射表2.1 分组方式与筛选结论我按考点把 12 道高频题分成四组字符串与滑动窗口LC3 无重复字符的最长子串、LC5 最长回文子串。链表 / 哈希与缓存LC206 反转链表、LC146 LRU 缓存。树与网格遍历LC199 二叉树的右视图和 LC102 层序遍历算同一组模板、LC236 二叉树的最近公共祖先、LC200 岛屿数量。二分答案、动态规划、双指针与区间LC875 爱吃香蕉的狒狒、LC494 目标和、LC15 三数之和、LC56 合并区间、LC42 接雨水。这里要解释一下为什么表面写了 13 个题号却说 12 道LC102 层序遍历和 LC199 右视图本质是同一套“按层处理”的模板很多面经提到“二叉树右视图”时候选人只要掌握了 LC102 的队列分层写法改一行就能应付 LC199所以我在盘点里把它们合并成一个位置。2.2 高频题速览表分组题号与题名难度核心考点可映射的客户端场景滑动窗口LC3 无重复字符的最长子串中等哈希表 双指针渲染帧内指令去重、日志关键字追踪字符串LC5 最长回文子串中等中心扩展 / 动态规划文本解析、对称格式校验链表LC206 反转链表简单指针操作、递归边界链式事件回放、消息队列反转链表 哈希LC146 LRU 缓存中等双向链表 HashMap图片内存缓存、磁盘资源淘汰网格遍历LC200 岛屿数量中等DFS / BFS、连通区域图像连通域、画布区域填充BFS 分层LC199 右视图LC102 层序中等队列按层遍历View 树层级遍历、导航栈重建树递归LC236 最近公共祖先中等后序递归 / 父指针视图树中找共同父容器二分答案LC875 爱吃香蕉的狒狒中等值域二分、check 函数弱网重试阈值、帧耗时定位动态规划LC494 目标和中等01 背包思想资源分配、组合方案统计双指针去重LC15 三数之和中等排序 双指针、去重参数组合去重、多路归并区间排序LC56 合并区间中等排序 贪心合并日志区段合并、埋点聚合双指针 / 单调栈LC42 接雨水困难双指针、单调栈布局高度累计、视觉面积计算2.3 不要把困难题当成最高优先级很多同学刷“leetcode 热门 100 题”时都容易陷入一个误区刷得越难越安全。但从大量面经反馈看字节客户端一轮代码题大多停留在中等级别把简单题做得滴水不漏比硬啃 hard 更能拉开分差。接雨水放进这份清单不是因为它一定比 LRU 更常考而是它作为“一道题能串起单调栈、双指针、按列累计多种思路”的代表面试官想提高难度时经常挑类似结构。所以我的建议是先把 LC206、LC3、LC199 这类基础结构题练成肌肉记忆再花时间挑战 LC42 与 LC236。3. 逐题拆解这 12 道题到底在考什么、怎么答才有增量3.1 链表与缓存LC206 反转链表和 LC146 LRU 缓存先说 LC206。这道题难度不高却高频是因为它直接考察“指针操作会不会乱”。迭代解法大家都知道pre、cur、next 三指针先保存 next再改 cur.Next pre然后 pre 和 cur 同时前进最后返回 pre。但面试现场只有一部分候选人能一次写对常见翻车点包括空链表判断漏掉、反转结束后忘记把原头指针的 Next 置空、两个节点时写成死循环。建议提前把边界情况在脑子里过一遍链表为空、只有一个节点、两个节点、正常多节点分别怎么走。最好再准备一版递归写法因为面试官喜欢追问“递归怎么写调用栈深度是多少链表长度 10 万时会不会爆栈”能回答出“递归反转深度 O(n)输入不可控时优先考虑迭代避免栈溢出”这本身就是加分点。LC146 LRU 缓存是客户端方向必刷中的必刷。缓存设计与图片缓存、资源淘汰需求几乎是一比一复刻。正解是双向链表 哈希表哈希表保证 O(1) 查找双向链表保证 O(1) 删除尾部并把最近访问节点移到头部。为什么必须双向因为单链表只给一个节点时无法 O(1) 拿到它的前驱去删除。有些人用单链表加 prev 指针遍历也能完成功能但复杂度不再是 O(1)面试评价会立刻掉档。实现时还要注意 get 和 put 的细节key 已存在时需要更新值并移到头部不存在且容量满了要先淘汰尾部节点再插入新节点。capacity 为 1以及同一个 key 连续 put 两次都是最容易暴露 bug 的隐藏用例。我在实际对练时见过太多人主流程记住了却在“更新已存在 key”时忘了挪节点到头部这种小错在面试官眼里很扎眼。3.2 滑动窗口与字符串LC3 无重复字符的最长子串和 LC5 最长回文子串LC3 是滑窗题的代表非常适合面试热身。面试时的讲解路径最好这样走先给最朴素做法枚举每个起点向右扩展直到遇到重复字符复杂度 O(n^2)。再优化成滑动窗口right 不断右移并把字符最后出现位置记入 map当发现当前字符出现过且位置在窗口内left lastIndex就把 left 跳到 lastIndex 1每次用 right - left 1 更新答案。最容易写错的是把 left 直接更新成 lastIndex 1却忘了用 max 限制。如果重复字符出现在窗口左侧之外直接赋值可能把左指针往回调。建议拿 abba 手动推一遍当 right 走到第二个 b 时如果 dead 记的不是“当前窗口内的位置”left 就可能错误回退。这类隐蔽问题正是面试官想观察的边界敏感度。LC5 最长回文子串推荐首选“中心扩展”而不是直接上动态规划或 Manacher。原因很简单中心扩展的代码直观边界也容易解释。以每个下标为中心向左右扩展注意回文中心可以是一个字符也可以是两个字符之间因此要分两种起点情况整体实现是两层循环复杂度 O(n^2)空间 O(1)。动态规划也可以写但递推数组的填充顺序容易绕晕。Manacher 能做到 O(n)适合作为口头补充表示你知道更优解但不建议在准备不充分时现场硬写。回文题之所以高频是因为它考你对“奇数长度中心 / 偶数长度中心”这类对称细节是否敏感这跟做字符串解析、排版测量时的细心程度很相关。3.3 树的队列分层与公共祖先LC199 右视图、LC236 最近公共祖先看树的问题时可以把客户端场景切换成 View 树一个根布局下面挂了几层子 View你想找到某一层最右侧的节点这就是二叉树的右视图。标准解法是 BFS 层序遍历核心是用队列记录当前层的节点数量。关键点在于每次外层循环开始时先记录 queue.size()然后只处理 size 个节点处理完这一层后队列里剩下的自然就是下一层。为什么必须固定 size如果不分层队列会混入下一层节点最后一个位置就无法代表“当前层的最右节点”。很多候选人知道 BFS但忽略了按层取 size 这个细节写出的代码只能碰巧通过简单用例。LC236 最近公共祖先常考递归后序遍历。递归逻辑很清晰当前节点为空、等于 p 或等于 q就返回当前节点否则递归左子树、右子树如果左右都不为空说明当前节点就是 LCA只有一边不为空就返回非空的那一边。这里要特别提醒当 p 是 q 的祖先时递归会在 p 处直接返回不会继续向下找 q所以入口不需要额外判断这正是递归解法的优雅之处。面试官爱追问“为什么不用记录路径再对比”你可以回答记录路径需要 O(n) 空间而后序递归空间是树高 O(h)多数情况下更省代码也短。能讲出这个差别很容易成为加分点。3.4 网格世界与连通区域LC200 岛屿数量网格 DFS/BFS 是很多客户端候选人容易忽略的部分。矩阵在内存里是连续区域在题目里常被类比为像素块。LC200 岛屿数量的思路很直接遍历每个格子碰到陆地就把岛计数加一然后从该位置出发把整块连通陆地全部标记为已访问。两种写法各有取舍。DFS 递归向上下左右扩展代码最短但极端情况下如果岛屿面积巨大递归深度会随矩阵规模上升有爆栈风险。BFS 用队列逐层扩散代码稍长但更稳。面试时主动说一句“我选择 BFS避免大岛屿时递归过深”比默默写完更有工程感。四个方向移动建议写成方向数组不要手写四段重复的递归调用否则复制粘贴很容易漏改坐标。如果还有余力可以把这道题扩展成“计算最大岛屿面积”“统计封闭岛屿数量”因为模板基本一致练熟一个就相当于拿下了一小类题。3.5 二分答案与阈值思维LC875 爱吃香蕉的狒狒LC875 几乎是“二分答案”的入门必刷题。很多人第一次见容易往贪心或模拟上想但更自然的解释是题目要求的是“最小速度”速度下界是 1上界是最大堆香蕉数我们要在值域里找第一个能让狒狒在 h 小时内吃完的值。这个有序性来自“速度越大吃完越早”所以可以用二分搜索左边界。canEat 函数是按速度 mid 模拟每一堆需要 ceil(pile / mid) 小时写成 (pile mid - 1) / mid 可以避免浮点误差总耗时累加后与 h 比较。二分循环里如果总耗时 h说明 mid 可能可行继续往左收缩搜索更小速度否则向右。容易被问的三个问题要提前准备总耗时会溢出吗pile 很大时用 long 累加更稳h 小于数组长度时是不是可以直接返回如果每小时至少吃一堆都吃不完说明输入本身不合理可以先判断为什么上界是 max(pile) 而不是 sum(pile)因为每小时最多只在一堆里吃速度超过最大堆没有意义。这道题和客户端开发的关联不在香蕉而在“不知道具体阈值时用二分逼近”弱网下自动重试间隔、帧耗时异常阈值定位都能套这个模板。3.6 动态规划与背包思想LC494 目标和LC494 看起来像 DFS 枚举组合最朴素做法是每个数都尝试正号或负号最坏 2^nn 稍大就会超时。关键优化是转 01 背包设数组和为 sum选中设为正数的集合和为 P则负数的集合和为 sum - P。目标等式是 P - (sum - P) target所以 P (sum target) / 2。只要 sum target 是偶数且 target 绝对值不超过 sum问题就变成“从 nums 中选出若干个数使和为 P 的方案数”。转移方程 dp[j] dp[j] dp[j - num]初始化 dp[0] 1。写代码时注意两个细节一是遍历 nums每个数字只能用一次所以内层 j 要从后往前更新二是如果 nums 里有 0要理解 dp[0] 累乘的语义不能简单跳过。候选人在“目标和”上翻车多数不是不会 DP而是没看出它是一个“固定预算下的组合计数”问题。建议刷完这道题后顺手练“分割等和子集”只要掌握了 01 背包的一维更新顺序这类题基本能串成一条线。3.7 排序、去重与边界LC15 三数之和和 LC56 合并区间LC15 三数之和的高淘汰点不是算法而是去重。排序之后固定第一个数再用双指针找后两个数这是主框架。但去重一共有三处外层当前数与前一个数相同时跳过找到一组答案后右指针向左跳过相同值左指针向右也跳过相同值。很多人只记得第一处导致结果里出现重复三元组。如果面试官追问“为什么不用哈希表”你要能答出来哈希表可以做两数之和但三数之和里直接哈希去重容易产生重复组合排序 双指针可以把去重做得更干净同时不需要额外空间。还有一个优化点固定数大于 0 时可以提前 break因为排序后后面都比它大和不可能为 0。LC56 合并区间在客户端开发里的类比是日志区段合并、界面块排序。思路很简单先按区间 start 升序排序维护当前区间 [left, right]遍历每个新区间如果 next.start 大于 right就把当前区间收进结果然后更新当前区间否则更新 right max(right, next.end)。容易忽略的是“先排序”以及“等于算不算重叠”要按题目语义判断。很多区间题用闭区间LC56 中 next.start right 时应该合并但如果你自己设计接口面对左闭右开的时间段逻辑就不同了。面试时能和面试官聊清楚这个假设比闷头写代码印象更好。3.8 有难度的双指针LC42 接雨水接雨水虽然是 hard但面经里出现的次数很稳定因为它能同时考察单调栈、双指针、空间压缩面试官想放一道难度可调题时常选它。建议答题时先讲最直观的做法每个位置能接的雨水等于它左侧最大高度和右侧最大高度中的较小值减去当前高度所以可以预处理 leftMax 和 rightMax 数组空间 O(n)。这不是最优解但先讲它能让面试官听懂你的第一反应是正确的然后再提出双指针把空间压到 O(1)。双指针核心思想是维护左右两侧已经扫过部分的最高值 leftMax 和 rightMax当 height[left] height[right] 时左指针位置的存水量只由 leftMax 决定因为右侧至少有 height[right]