拓冰建站拓冰建站
首页 / 资讯中心 / 正文

面试常考算法题详解:双指针、滑动窗口、链表、二叉树与动态规划

面试算法题这件事我一直有个观点刷题数量和面试通过率从来都不是线性关系。见过太多人把LeetCode刷了五六百题结果面试官换一道变形题就卡住也见过有人只刷了一两百题但每道题都能把思路讲明白把边界条件说清楚反而顺利拿下offer。问题不在题量在方法。这篇是“面试常考算法题”系列的第一篇先聊最核心的高频题型双指针、滑动窗口、链表、二叉树、动态规划。这些都是技术面试中出现频率最高的方向无论你面Java、Python、前端还是测试开发这几类题型几乎是必考的。我会从“面试官到底想考什么”出发拆解每类题型的底层逻辑、解题模板、以及现场写码时最容易翻车的细节。内容偏实战适合正在准备面试的读者也适合带新人的技术leader做参考。1. 面试算法题到底在考什么先把考官手里的评分表看明白很多候选人有一个错误的预设面试算法题就是考“你会不会做这道题”。实际上技术面试官考察的从来不是“答案正确”而是“你如何得到这个答案”。1.1 技术面候选人的四项基本能力我参与过不少校招和社招的面试也和其他面试官交流过对候选人的评估标准。综合来看一道算法题在面试现场至少承担了以下四个维度的考察问题澄清能力拿到题目后是直接闷头写还是会先确认输入范围、数据规模、是否存在重复元素、是否有序。这一步能筛掉一大批人。边界处理意识空数组、单元素、极大值、溢出情况候选人是否主动想到。这直接反映工程习惯。复杂度分析能力面试官会问“这个解法的时间复杂度和空间复杂度是多少”很多人能写出代码但分析不清楚。沟通与协作能力你是一个人在白板上默写还是会边写边把思路说出来遇到卡顿会不会主动和面试官交流。这决定了你入职后是否好合作。1.2 “八股文”式背题的误区现在网上流行各种“面试八股文”“刷题模板”这些内容作为入门没问题但最大的问题在于只给了答案没有给推导过程。面试官只要把原题稍微改一个条件比如把“数组”改成“链表”把“整数”改成“字符串”把“求最大值”改成“求最小值”背模板的人就露馅了。面试官日常看到的场景是候选人A上来就开始写代码写的确实是对的但问“为什么这样不会越界”答不上来候选人B先花两分钟确认数据规模说“如果数组长度是十万O(n²)会超时所以我需要O(n)方案”然后给出思路。哪怕B最后代码有小bug面试评价往往也高于A。1.3 高频算法题的三大来源从面试官出题的角度看算法题基本有三个来源经典教材题比如《剑指Offer》和LeetCode Hot 100里的题。这些题考察的算法思想基础区分度好大家默认候选人应该掌握。经典题的变形原题换个壳考察候选人能否识别出本质。例如“最小覆盖子串”是滑动窗口“寻找两个正序数组的中位数”是二分边界处理。结合业务的场景题比如“海量日志中统计Top K”“检测循环引用”这类本质还是堆、哈希表、快慢指针但包装了实际业务背景。看清这一点对准备面试很关键你需要练习的不是“记住这道题的答案”而是“识别这道题背后的算法模式”。这也是本文所有拆解的核心思路。2. 数组与双指针面试中出现频率最高的送分题也是失分重灾区数组类问题几乎每场面试都会遇到。而处理数组最常用的技巧之一就是双指针。2.1 双指针算法到底在解决什么问题双指针的核心价值是用两个指针的移动替代一层循环把时间复杂度从O(n²)降到O(n)。最典型的场景是“有序数组中找两数之和”这类题目。举个例子给定一个有序数组和一个目标值找出数组中两个数使它们的和等于目标值。暴力做法是两层循环枚举所有组合时间复杂度O(n²)。双指针做法是一个指针指向数组头部一个指针指向尾部计算两者之和如果大于目标值说明需要减小和右指针左移如果小于目标值说明需要增大和左指针右移。这样每个元素最多被访问一次时间复杂度O(n)。$$ two_sum(nums, target):\ \larr \text{ } i0, jn-1\ \text{if } nums[i]nums[j] target: return [i, j]\ \text{else if } nums[i]nums[j] target: i \mathrel{} 1\ \text{else: } j \mathrel{-} 1 $$这个算法思路非常简单但我在面试中看到大量候选人栽在同一个地方写代码时没有确认数组是否有序。如果题目没说明有序双指针法直接失效必须先排序但排序会改变索引所以涉及返回索引的题需要额外的处理。2.2 快慢指针原地去重与环检测双指针的另一个重要分支是快慢指针。面试中出现频率极高的“有序数组原地去重”标准解法就是快慢指针。def remove_duplicates(nums): if not nums: return 0 slow 0 # slow指向最后一个不重复元素的位置 for fast in range(1, len(nums)): if nums[fast] ! nums[slow]: slow 1 nums[slow] nums[fast] return slow 1这段代码的含义是slow指针维护“已处理区域”的边界fast指针负责探索新元素。每次发现新的不重复元素就把它搬到slow的下一个位置最终slow1就是去重后的数组长度。核心思想是“用一个指针维护结果区域另一个指针遍历原数组”。同样的思想可以迁移到“移动零”这道题把数组中所有0移到末尾同时保持非零元素的相对顺序。思路完全一致slow维护非零区域的边界fast遍历数组遇到非零就交换到slow位置。2.3 面试中的真实翻车场景我在模拟面试中见过一位候选人做“判断链表是否有环”这道题背过答案能写出fast走两步、slow走一步的代码但被问到“为什么fast必须走两步走三步行不行”时卡住了。这个问题其实考察的是对快慢指针原理的理解。假设链表有环环的长度为L当slow进入环时fast已经在环内某处。如果fast每次比slow多走一步那么两者的相对速度是“每步接近1个节点”最终一定能相遇。如果fast每次比slow多走三步相对速度是3当环长度L能被3整除时两者可能永远追不上每次都跳过。所以标准答案fast走两步、slow走一步是保证“一定能相遇”的最小安全速度。这类“为什么”问题是面试官区分“背题”和“真懂”的关键。准备算法题时建议对每道做过的题都问自己一遍这个解法为什么是对的能不能举个例子证明它不会死循环2.4 双指针题的面试话术与边界意识面试现场写双指针题建议按以下节奏展开先确认条件“请问数组是有序的吗数据规模大概是多少能否使用额外空间”即使题目已经写明也最好口头确认一遍这能给面试官留下严谨的印象。给出暴力解并分析复杂度“我可以先用两层循环O(n²)但数据量大时会超时所以我考虑用双指针把复杂度降到O(n)。”说明正确性依据“因为数组有序当左右大于target时右指针左边的任何元素加上当前位置都只会更大所以右指针左移不会漏解。”这里把数学依据说清楚是加分项。写代码时关注边界数组为空、只有一个元素、两个指针相撞时的退出条件。写完主动提测试用例空数组、恰好一正一负、全是相同元素。这套流程等于把面试官想问的问题抢先说了出来整个面试节奏就会被你掌控。3. 滑动窗口把“子串子数组”问题变成一套模板“无重复字符的最长子串”“最小覆盖子串”“长度最小的子数组”——这些题本质都是一个模式在一个线性结构上维护一个动态的区间区间满足某个条件要求区间的最大或最小长度。这类题的最优解十有八九是滑动窗口。3.1 什么时候该想到滑动窗口判断一道题是否适用滑动窗口看两个特征考察对象是连续的子串/子数组不是子序列子序列通常用动态规划。题目中有“最长/最短/恰好包含”这类关键词且窗口的状态可以通过两个端点来描述。举个例子“给定一个数组nums和一个正整数s找出满足其和≥s的长度最小的连续子数组”。暴力做法是枚举所有子数组O(n²)。滑动窗口的做法是右指针不断扩张窗口当窗口内和满足条件时记录长度然后左指针收缩窗口寻找更短的合法窗口。3.2 一个通用滑动窗口模板滑动窗口的代码逻辑几乎都是一样的核心是维护窗口内数据的哈希表或计数器根据条件决定窗口何时扩张、何时收缩。def sliding_window(s, target_condition): n len(s) left 0 window {} # 维护窗口内元素的计数 ans 0 # 根据题目要求更新 for right in range(n): # 1. 将s[right]加入窗口 window[s[right]] window.get(s[right], 0) 1 # 2. 当窗口不满足条件时收缩左边界 while not condition(window): window[s[left]] - 1 if window[s[left]] 0: del window[s[left]] left 1 # 3. 此时窗口满足条件更新答案 ans max(ans, right - left 1) return ans“无重复字符的最长子串”套这个模板条件就是“窗口内所有字符计数都为1”。def length_of_longest_substring(s: str) - int: n len(s) left 0 window {} ans 0 for right in range(n): window[s[right]] window.get(s[right], 0) 1 while window[s[right]] 1: window[s[left]] - 1 left 1 ans max(ans, right - left 1) return ans3.3 窗口伸缩的平衡条件与答案更新时机滑动窗口最容易出错的地方是while收缩的时机和答案更新的时机。很多候选人在这个细节上翻车。原则是这样的答案是“某个满足约束的窗口的宽度”但需要区分是最大窗口还是最小窗口。如果求“最长”比如最长无重复子串窗口扩张后如果满足条件就可以尝试更新答案如果不满足就收缩窗口直到满足条件收缩完再更新。如果求“最短”比如最短子数组和窗口扩张后如果不满足条件继续扩张一旦满足条件就先把当前窗口宽度记录下来候选答案然后收缩窗口试图找到更短的满足条件的窗口每次收缩后如果仍满足条件继续更新答案。一个容易踩的坑是“窗口收缩到什么时候停”。以“最小覆盖子串”为例答案是包含目标字符串所有字符的最短子串。窗口收缩的条件是“当前窗口仍然包含目标字符串的所有字符”一旦不满足就停止收缩继续右移。很多候选人会把条件写成“当前窗口长度大于目标字符串长度”这只有在特定题型下才成立不能通用。3.4 面试实战先讲“为什么right左移是安全的”滑动窗口的难点不在代码在于论证滑动窗口不会漏掉最优解。面试官大概率会问“你这个做法为什么是对的为什么滑动窗口不会漏掉一个更长的合法子串”回答思路是当窗口[left, right]已经满足条件时如果左指针向右移动缩小窗口后窗口不再满足条件说明以这个新left为起点的所有子串中最短的合法子串就是当前窗口之前的那个len(right-left2)。因为right是当前遍历到的位置窗口缩到不满足条件所需的宽度就是当前起点下能达到的最小宽度。因此不需要再从left1开始重新枚举直接推进right即可。把这段逻辑清晰地说出来面试官对你的评价会大幅提升。这是滑动窗口和暴力解之间“优化逻辑”的核心也是很多人只会写代码、讲不出道理的地方。4. 链表操作画图比背代码重要得多链表在面试中的出现频率极高而且几乎都是送分题但失分率依然很高。为什么因为链表的操作涉及大量指针或引用的重新指向边界条件多稍不注意就出现空指针异常或死循环。4.1 链表题失分的三个典型原因我总结过候选人做链表题时的常见问题不画图直接写。链表操作是典型的“空间想象题”不画图靠脑补多半会错。忘记处理头节点。反转链表后新的头节点是原来的尾节点很多人在返回时直接返回head导致结果错误。指针覆盖顺序搞反。比如删除节点时先修改了next导致后面的节点丢失。4.2 哨兵节点统一边界逻辑的利器链表操作中最让我推荐的习惯是引入哨兵节点dummy node。哨兵节点是一个虚拟的头节点它的next指向真正的头节点。这样做的好处是不需要单独处理“操作位置在头节点”的情况统一逻辑。举例“删除链表中倒数第N个节点”。如果不用哨兵节点删除头节点的逻辑和删除中间节点的逻辑是分开的容易漏。用哨兵节点代码就变成def remove_nth_from_end(head, n): dummy ListNode(0, head) fast dummy slow dummy # fast先走n1步这样当fast走到None时slow正好在倒数第n个节点的前一个 for _ in range(n 1): fast fast.next while fast: fast fast.next slow slow.next slow.next slow.next.next return dummy.next这个解法用到了“快慢指针找倒数第N个节点”“哨兵节点统一边界”两个技巧非常经典。面试中一旦写出这个结构基本就是满分答案。4.3 三个必会的链表模板链表题看似花哨但真正高频的模板就三个模板一反转链表迭代版def reverse_list(head): prev None curr head while curr: next_node curr.next # 先保存下一个节点 curr.next prev # 反转指针 prev curr # prev前进 curr next_node # curr前进 return prev反转链表是很多链表题的基础比如“回文链表”“反转链表的一部分”“两数相加”都会用到。核心逻辑就是三行保存next、修改next指向、移动prev和curr。面试时一定要把这三行写在纸上对照图说清楚。模板二找链表中点快慢指针def middle_node(head): slow fast head while fast and fast.next: slow slow.next fast fast.next.next return slow注意这里循环条件是fast and fast.next如果写成fast.next and fast.next.next也会出问题链表长度为偶数时返回的位置会不同。具体是返回左中点还是右中点取决于题目要求面试时最好口头确认。模板三合并两个有序链表def merge_two_lists(l1, l2): dummy ListNode(0) tail dummy while l1 and l2: if l1.val l2.val: tail.next l1 l1 l1.next else: tail.next l2 l2 l2.next tail tail.next tail.next l1 if l1 else l2 return dummy.next这里用哨兵节点dummy来构建新链表最后直接返回dummy.next省去了判断“哪个链表先遍历完”的额外逻辑。合并链表是“合并K个有序链表”“排序链表”等题的基础必须熟练到能默写。4.4 现场写链表题最容易踩的坑指针覆盖顺序我在模拟面试中反复看到的一个错误是在反转链表时没有保存next_node就直接修改curr.next导致后面节点全部丢失。错误写法 while curr: curr.next prev # 先改了next原链表断裂 prev curr curr curr.next # 此时curr.next已经是prev了不再是原next这个错误非常隐蔽因为代码看起来逻辑通顺但执行一遍就会发现无限循环或链表丢失。链表操作的黄金法则是先保存后修改。任何需要修改某个节点.next的操作先把原来的next保存到临时变量再动手改。面试时养成这个习惯能避开大部分链表bug。另一个容易踩的坑是返回头节点。反转链表时新的头节点是prev不是head。很多人写了半天最后return head返回的是尾节点反转后head的next已经指向None导致整个链表看起来像空链表。4.5 实战案例复盘两数相加的“逐位模拟”怎么聊“两数相加”这道题也是链表中的高频题给两个非空链表表示两个非负整数数字按逆序存储每个节点存一位数求两数之和同样以链表形式返回。这道题的本质是模拟竖式加法。核心变量有三个p1和p2分别遍历两个链表一个carry变量存储进位。每次循环计算val p1.val p2.val carry新节点的值为val % 10进位为val // 10。循环结束后如果carry不为0还要额外补一个节点。面试时这道题的分寸在于是否主动聊到两个链表长度不等的情况。可以这样说“如果其中一个链表遍历完了另一个还有剩余节点我只需要把剩余节点和进位继续相加所以循环条件是while p1 or p2 or carry。”这句话一出来面试官就知道你考虑过边界条件。5. 二叉树递归、迭代与层层推进的抽象能力二叉树是面试算法题的“常青树”。原因在于它考察的不是死记硬背而是递归思维的熟练度以及将递归改写为迭代的能力。二叉树的高度、宽度、路径、最近公共祖先等都是面试官的心头好。5.1 递归三要素写之前先问自己三个问题二叉树题目90%都可以用递归解决。递归的写法有固定套路我称之为“递归三要素”这个函数的定义是什么入参、返回值、要完成的事。递归的终止条件是什么通常是节点为空。当前层要做什么处理当前节点、递归调用左右子树、汇总结果。以“求二叉树最大深度”为例def max_depth(root): if root is None: return 0 left_depth max_depth(root.left) right_depth max_depth(root.right) return max(left_depth, right_depth) 1三要素在这里非常清晰函数定义是“计算以root为根的树的最大深度”终止条件是root为空返回0当前层要做的是“分别求左右子树深度取较大值再加1”。只要三要素想清楚了递归代码基本不会写错。5.2 递归转迭代栈模拟的底层逻辑面试官经常会在你写完递归后追加一问“如果不让用递归你还能写吗”这考查的是对栈的理解。以二叉树的中序遍历为例递归写法是def inorder(root): if not root: return [] return inorder(root.left) [root.val] inorder(root.right)改成迭代需要手动模拟栈def inorder_iter(root): res [] stack [] cur root while cur or stack: while cur: stack.append(cur) cur cur.left cur stack.pop() res.append(cur.val) cur cur.right return res这里的核心思想是while cur不断往左走并压栈相当于递归调用左子树弹出栈顶节点相当于“递归返回后处理当前节点”然后转向右子树相当于进入右子树的递归。理解了这一点前序和后序的迭代写法也能推出来。5.3 层序遍历模板按层输出的通用解“按层输出二叉树的节点值”也是高频题解法是BFS队列def level_order(root): if not root: return [] res [] queue [root] while queue: level_size len(queue) level [] for _ in range(level_size): node queue.pop(0) level.append(node.val) if node.left: queue.append(node.left) if node.right: queue.append(node.right) res.append(level) return res关键点在于每次循环前先取len(queue)这个长度就是当前层的节点数。这样循环内处理的就是当前层循环末尾新入队的都是下一层的节点。很多候选人直接在循环里pop而不先记录level_size会导致层与层之间的节点混在一起。5.4 递归里修改全局变量的隐藏坑二叉树递归题里有一类“返回所有路径”的题比如“二叉树的所有路径”“路径总和Ⅱ”。这类题需要在递归过程中收集中间结果最容易出现的问题是用同一个列表变量在不同分支间共享导致回溯时数据错乱。正确做法是每次递归时传递当前路径的一个新副本或者递归返回后手动回溯删除刚添加的节点。面试时我建议优先用“新副本”的方式因为逻辑清晰不容易错但也要理解手动回溯的写法因为某些场景下新副本空间开销大面试官可能会追问优化方案。典型错误# 错误拿同一个path列表传给左右子树导致分支间数据污染 def dfs(root, path): if not root: return path.append(root.val) if not root.left and not root.right: res.append(path) dfs(root.left, path) dfs(root.right, path)正确写法之一是def dfs(root, path): if not root: return new_path path [root.val] if not root.left and not root.right: res.append(new_path) return dfs(root.left, new_path) dfs(root.right, new_path)注意path [root.val]在Python中生成的是新列表不会影响其他分支。如果面试官要求优化空间可以改成回溯写法加入节点、递归、删除节点三个动作配对出现。6. 动态规划从“背转移方程”到“推导转移方程”动态规划是面试算法题里最让人头疼的部分。它不像双指针或链表那样有明确的代码模板每道题的转移方程都不一样。但也正因为如此它是区分候选人算法功底的关键分水岭。6.1 动态规划题的第一步定义状态我在面试中观察到的最大问题是很多候选人一上来就想“转移方程是什么”然后对着方程硬套结果变形题就崩了。正确的思考顺序是先想清楚“状态”怎么定义再想状态之间怎么转移。以“打家劫舍”为例你是一个小偷沿街盗窃不能偷相邻的两家求能偷到的最大金额。暴力做法是枚举所有偷或不偷的组合复杂度O(2^n)。动态规划的做法是定义状态dp[i]表示“从前i个房屋中能偷到的最大金额”。注意这个定义隐含了一个决策对于第i个房屋要么偷要么不偷。如果不偷第i个那dp[i] dp[i-1]如果偷第i个那第i-1个不能偷dp[i] dp[i-2] nums[i]。所以转移方程是$$ dp[i] \max(dp[i-1], dp[i-2] nums[i]) $$初始化$$ dp[0] nums[0],\quad dp[1] \max(nums[0], nums[1]) $$整个推导过程只有四步定义状态、确定转移、设定初始值、确定遍历顺序。每道DP题都可以套这个框架。6.2 为什么“状态定义”是最容易卡住的地方状态定义没有统一模板但有一些常见套路一维线性DPdp[i]表示前i个元素的结果比如“爬楼梯”“打家劫舍”“最长递增子序列”。二维区间DPdp[i][j]表示区间[i, j]的结果比如“最长回文子串”“戳气球”。背包类DPdp[i][j]表示前i个物品、容量为j时的最优值比如“0-1背包”“分割等和子集”。状态机DPdp[i][0/1]表示第i天在某种状态下的最优值比如“买卖股票的最佳时机”系列。面试考到动态规划时如果你能说出“这道题的状态定义是xxx因为它可以划分为xxx子结构”哪怕转移方程推导慢一点面试官也会认可。最怕的是连状态都定义不出来直接陷入沉默。6.3 面试现场动手推导和口头推演现场写DP题我建议遵循一个执行顺序先举一个小例子手动推演。比如“打家劫舍”里nums[2,7,9,3]手动写出dp数组[2,7,11,11]。这个小例子不仅能帮自己理清状态也能让面试官看到你的推导过程。把转移方程用自然语言说出来。比如“当前最大金额要么是前一家的最大金额要么是前两家加上当前这家”。再写代码。这样面试官看到的是“你在解题”而不是“你在默写答案”。写完代码后用手动推演的例子跑一遍。这一步非常加分能顺带验证数组下标是否越界。6.4 DP空间优化的常见手法面试官在DP题后的常见追问是“空间复杂度能优化吗”。大多数一维DP都可以用滚动数组把O(n)优化到O(1)。以“打家劫舍”为例def rob(nums): prev2 0 # dp[i-2] prev1 0 # dp[i-1] for num in nums: cur max(prev1, prev2 num) prev2 prev1 prev1 cur return prev1这里只保留了前两个状态。滚动数组的本质是转移方程只依赖前几个状态所以不需要保存整个数组。这个技巧在面试中非常实用对二维DP还可以用“滚动行”的方式优化空间。6.5 实在不会做时怎么“止损”面试现场如果完全没思路有几个保底策略先说暴力解法比如“我可以先枚举所有子集复杂度2^n数据规模小的时候能过”。至少能拿一部分分。尝试递归备忘录写一个暴力递归如果发现存在大量重复子问题就加一个缓存数组memo这就是记忆化搜索很多DP题用记忆化搜索也能通过。而且记忆化搜索比递推更容易写对适合临场发挥。和面试官沟通可以说“我目前想到的是暴力解法感觉有重复计算但还没想清楚怎么用DP优化您能给我一点提示吗”面试官通常会给你一个方向比如“你觉得当前决策是否只依赖前一个状态”。我见过不止一个候选人用“暴力递归记忆化”把一道DP题写出来了最终评价并不差。面试官给分从来不是只看最优解而是看你的思维过程。7. 刷题路线与复盘方法把“做过”变成“会做”前面讲了五大类高频题型的核心逻辑但还有一个更现实的问题时间有限到底怎么安排刷题计划我的建议是按题型刷不按题号刷。7.1 按题型刷而不是按热度刷LeetCode的“热题100”适合入门感受难度但真正高效的刷法是按题型模块化推进。比如花一周专门刷双指针和滑动窗口再花一周专门刷链表接着二叉树然后动态规划。每类题型集中刷10-20道总结出通用模板和思维套路。这样做的好处是你能在短时间内积累大量同类题自然而然地归纳出“这类题的解法套路”。如果你今天刷一道链表、明天刷一道动态规划大脑无法形成有效的模式识别刷100题可能还是混乱的。7.2 每道题刷三遍的正确打开方式我个人的经验是一道有价值的题至少刷三遍。第一遍不看答案尝试独立写出暴力解或能想到的最优解。如果写不出来看题解理解思路后自己关掉题解重写一遍。第二遍过2-3天后重做这道题只要求能把思路讲清楚代码写出来尽量优化到最优解。第三遍一周后盲写代码并准备一段“为什么这样做是对的”的口头解释。这三遍的目的分别是建立初步认知、巩固思路、形成条件反射。特别是第三遍的口头解释直接对应面试场景很多人笔试时能写出代码但面试现场说不清楚就是缺乏这一遍练习。7.3 建立自己的“一句话题解”库我在准备面试时会用一个表格维护自己的刷题记录大概长这样题目核心考点一句话题解易错点无重复字符的最长子串滑动窗口右指针扩张窗口内出现重复则收缩左指针更新最大宽度收缩条件写错反转链表链表prev/curr/next三指针逐节点反转返回prev指针覆盖顺序最大子数组和DP/贪心dp[i]max(nums[i], dp[i-1]nums[i])初始化合并两个有序链表链表dummy哨兵节点tail依次连接较小子节点返回dummy.next这个表格的价值在于考前复习效率极高。你不需要把所有代码重写一遍只需要看“一句话题解”和“易错点”就能快速唤起记忆。7.4 面试前一周到底该做什么面试前一周不建议再刷新题而是做三件事重刷高频题把前面整理的“一句话题解”表里标红的高频题全部手写一遍重点检查边界条件和复杂度分析。口头复述思路找一个人或者对着录音设备随机抽题用30秒说思路然后写代码。模拟真实面试的节奏。整理自己的“失误清单”把做错过的题、踩过的坑汇总成一份清单考前过一遍。我见过太多人在面试中重复犯平时刷题时犯过的错就是因为没有把错题整理出来。结尾最后分享一个我个人的小习惯刷完每一道题找一个完全没做过这道题的人把解题思路讲给他听。如果在讲的过程中你能让他听懂那这道题才是真的掌握了。如果讲着讲着自己卡住了那基本就是某个边界条件或某个为什么没想清楚赶紧回去补。面试常考算法题一先写到这。这期聊的双指针、滑动窗口、链表、二叉树、动态规划是面试中出现频率最高的几大方向。后续我打算再写一篇专项内容把二分查找、堆/优先队列、图的遍历、回溯算法这类同样高频的方向拆开讲。算法面试这东西说到底是“刷题的数量决定下限复盘的质量决定上限”一起加油。
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门