副标题 / 摘要 这题表面上像字符串题,实质上是一个非常标准的固定层数回溯模型:第 k 层只处理第 k 个数字,从其映射字母里选一个,直到路径长度等于输入长度。 预计阅读时长:10~12 分钟 标签:Hot100、回溯、字符串、DFS SEO 关键词:Letter Combinations of a Phone Number, 电话号码的字母组合, 回溯, DFS 元描述:用 LeetCode 17 建立固定层数 DFS 模板,理解字符映射、路径长度终止与多语言实现。 目标读者 已经掌握 78 / 46,准备看另一类回溯树形态的学习者 想把“每层处理一个位置”这种 DFS 模型固定下来的开发者 需要做编码扩展、短串生成、候选串组合的工程师 背景 / 动机 这题和子集、排列都不太一样。 子集题:每层决定“要不要继续选后面的元素” 排列题:每层决定“当前位置放哪个未使用元素” 本题:每层对应一个固定数字位置,只能从该数字映射的字母中选一个 因此它非常适合训练“固定深度 DFS”: 递归层数由输入长度决定 每一层的候选集由当前字符直接决定 路径长度等于输入长度时结束 这类模型在字典枚举、编码扩展、模板字符串生成中很常见。 核心概念 数字映射表:2 -> abc, 3 -> def, …, 9 -> wxyz 固定层数 DFS:第 index 层只处理 digits[index] 叶子条件:index == len(digits) 时得到一个完整答案 路径构建:每层向路径追加一个字符,返回时撤销 A — Algorithm(题目与算法) 题目还原 给定一个仅由数字 2 到 9 组成的字符串 digits,返回它能表示的所有字母组合。 答案顺序不限。数字与字母的映射与电话按键一致,1 不对应任何字母。 ...
Hot100:全排列(Permutations)used[] 状态回溯模板 ACERS 解析
副标题 / 摘要 如果说子集题教你“组合型回溯”的骨架,那么全排列题教你的就是“状态型回溯”的核心:当前位置要选一个还没用过的元素,直到路径长度等于 n 才收集答案。 预计阅读时长:10~12 分钟 标签:Hot100、回溯、全排列、DFS SEO 关键词:Permutations, 全排列, 回溯, used, DFS 元描述:通过 LeetCode 46 固定排列型回溯模板,重点理解 used[]、叶子收集与多语言实现。 目标读者 已经做完 78. 子集,准备进入排列型回溯的学习者 会写递归,但状态恢复经常出错的开发者 需要枚举任务执行顺序、测试顺序或操作序列的工程师 背景 / 动机 排列问题和组合问题最本质的区别是: 组合只关心“选了哪些元素” 排列还关心“这些元素出现的顺序” 所以在全排列里,[1,2,3] 和 [1,3,2] 是两个不同答案。 这意味着你不能再靠 startIndex 只向后看,而必须显式记录“哪些元素已经用过”。 LeetCode 46 的价值就在这里:它把“状态恢复”这件事讲得非常干净。 核心概念 path:当前构造中的排列 used[i]:nums[i] 是否已经被当前路径使用 叶子收集答案:只有当路径长度等于 nums.length 时,才得到一个完整排列 状态撤销:递归返回时同时撤销 path 和 used A — Algorithm(题目与算法) 题目还原 给定一个不含重复数字的数组 nums,返回它的所有可能全排列。 答案顺序不限。 输入输出 名称 类型 描述 nums int[] 不含重复元素的整数数组 返回 int[][] 所有可能的全排列 示例 1 输入:nums = [1,2,3] 输出:[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]] 示例 2 输入:nums = [0,1] 输出:[[0,1],[1,0]] 示例 3 输入:nums = [1] 输出:[[1]] 提示 1 <= nums.length <= 6 -10 <= nums[i] <= 10 nums 中所有整数互不相同 C — Concepts(核心思想) 从子集题到全排列题,模板哪里变了 78. 子集 的关键是 startIndex,因为组合不关心顺序。 但全排列不同,每一层都可以从“所有还没用过的元素”里选一个,所以: ...
Hot100:子集(Subsets)回溯枚举 / startIndex 模板 ACERS 解析
副标题 / 摘要 子集是 Hot100 回溯专题里最适合打地基的一题。真正要固定下来的不是“把答案都列出来”,而是 path、startIndex 和“每个节点都是答案”这三个核心不变式。 预计阅读时长:10~12 分钟 标签:Hot100、回溯、子集、DFS SEO 关键词:Subsets, 子集, 回溯, startIndex, 幂集 元描述:用 LeetCode 78 子集建立最稳定的回溯模板,含工程场景、复杂度分析与多语言实现。 目标读者 刚进入 Hot100 回溯专题、想先把模板打稳的学习者 能写 DFS,但还没真正理解“组合”和“排列”区别的开发者 希望把枚举思路迁移到配置组合、策略试跑场景的工程师 背景 / 动机 “列出所有可能组合”在工程里并不少见。 比如功能开关组合试跑、权限策略候选集生成、前端筛选项预设等,本质上都在做“从若干候选元素里列出所有选择结果”。 这类问题最容易犯的错有两个: 把组合写成排列,导致重复答案 把“什么时候收集答案”放错位置,导致漏解 LeetCode 78 的价值就在于:它约束足够简单,没有重复元素,也不要求复杂剪枝,适合你先把回溯树的骨架搭稳。 核心概念 path:当前递归路径上已经选中的元素 startIndex:下一层从哪里开始选,保证组合不会倒序重复 前序收集答案:子集题里,每个节点本身就是一个合法答案 回溯撤销:递归返回后,要把刚加入 path 的元素弹出 A — Algorithm(题目与算法) 题目还原 给定一个元素互不相同的整数数组 nums,返回它的所有可能子集。 结果中不能包含重复子集,返回顺序不限。 输入输出 名称 类型 描述 nums int[] 元素互不相同的整数数组 返回 int[][] 所有可能的子集 示例 1 输入:nums = [1,2,3] 输出:[[],[1],[2],[1,2],[3],[1,3],[2,3],[1,2,3]] 示例 2 输入:nums = [0] 输出:[[],[0]] 提示 1 <= nums.length <= 10 -10 <= nums[i] <= 10 nums 中所有元素互不相同 C — Concepts(核心思想) 为什么它是回溯入门题 这题没有“目标和”、没有“判重数组”、也没有棋盘边界。 你只需要想清楚一件事: ...
Hot100:组合总和(Combination Sum)回溯剪枝 / 可重复选取 ACERS 解析
副标题 / 摘要 组合总和是回溯专题里第一道真正把“组合模板 + 目标约束 + 剪枝”揉在一起的题。你要学会的不只是枚举,而是怎样用排序和剩余值 remain 把搜索树收紧。 预计阅读时长:12~15 分钟 标签:Hot100、回溯、组合、剪枝 SEO 关键词:Combination Sum, 组合总和, 回溯, 剪枝, DFS 元描述:通过 LeetCode 39 建立组合型回溯加剪枝模板,理解可重复选取、排序与 remain 约束。 目标读者 已经做过 78. 子集,准备把回溯模板升级到“带约束搜索”的学习者 想搞清楚“同一个数可以重复使用”时递归边界怎么写的开发者 需要做资源打包、预算组合、规格拼装类组合搜索的工程师 背景 / 动机 这题是很多人真正开始理解“回溯不是暴力乱搜”的分水岭。 因为它同时有三件事: 仍然是组合问题,所以要保持顺序无关 候选数字可以重复使用 目标和 target 给了你天然剪枝条件 如果你只会硬搜,代码虽然也许能过,但模板不稳定。 而一旦你把“排序 + remain + 从 i 开始递归”的逻辑想清楚,这一类题都会顺很多。 核心概念 path:当前正在尝试的一组组合 remain:当前还差多少才能凑到目标值 从 i 继续递归:表示当前数字可以重复使用 排序剪枝:若 candidates[i] > remain,后面的数更大,可直接停止 A — Algorithm(题目与算法) 题目还原 给定一个无重复元素的整数数组 candidates 和一个目标值 target, 找出所有和为 target 的不同组合。 ...
先写骨架,再补细节:用契约拆解算法题与中型程序
围绕“公开接口先行、helper 以契约占位、实现围绕不变量展开”这条主线,系统讲解如何从算法题过渡到中型程序设计,并用 LRUCache 与下单流程示例说明它和 DDD 的分工关系。
克隆图:哈希表 + DFS/BFS 实现无向图深拷贝(LeetCode 133)
副标题 / 摘要 Clone Graph 不是单纯的图遍历题,而是“带环对象图的深拷贝”题。真正的关键不是能不能走完图,而是如何保证每个原节点只克隆一次,并且所有边都指向克隆图中的新节点。 预计阅读时长:12~15 分钟 标签:图、DFS、BFS、哈希表、深拷贝 SEO 关键词:克隆图, Clone Graph, 图深拷贝, LeetCode 133, DFS, BFS 元描述:通过“原节点 -> 新节点”映射表实现无向图深拷贝,讲清 DFS/BFS、环处理、复杂度与多语言代码。 目标读者 刷 LeetCode 图论题、希望掌握“深拷贝 + visited/map”模板的学习者 需要复制对象图、工作流图、拓扑结构的工程师 经常在“图遍历”和“对象复制”之间混淆的开发者 背景 / 动机 很多同学第一次做这题,会把它当成普通遍历题: DFS 一遍 BFS 一遍 把值抄过去 但真正难点在于: 图里可能有环 同一个节点可能从多条路径访问到 复制出来的新图,所有邻居必须指向“新节点”,不能混入旧节点引用 所以这题本质上是: 带环对象图的深拷贝问题 这类模式在工程里也很常见: 复制流程编排图 克隆编辑器里的节点网络 复制依赖关系图做快照 核心概念 深拷贝:返回的新图里每个节点都必须是新建对象 节点身份:判断“是不是同一个节点”看的是对象身份 / 引用,不只是 val 邻接关系保持:新图的边结构必须与原图完全一致 映射表:原节点 -> 克隆节点,既防止死循环,也防止重复创建 A — Algorithm(题目与算法) 题目重述 给定一个无向连通图中某个节点 node 的引用,请返回这个图的深拷贝。 每个节点结构如下: class Node { public int val; public List<Node> neighbors; } 题目测试用例使用邻接表表示图。 如果图不为空,给定节点总是值为 1 的节点。 ...
Hot100:二叉树的层序遍历(Binary Tree Level Order Traversal)BFS / DFS ACERS 解析
副标题 / 摘要 层序遍历是二叉树 BFS 模板的起点。真正关键的不是“用队列”,而是“如何把同一层的节点切分出来”。本文按 ACERS 结构拆解 LeetCode 102 的按层处理方法、DFS 深度分桶备选方案,以及工程里常见的分层遍历场景。 预计阅读时长:10~12 分钟 标签:Hot100、二叉树、BFS、DFS、队列、层序遍历 SEO 关键词:Hot100, Binary Tree Level Order Traversal, 二叉树的层序遍历, BFS, 队列, LeetCode 102 元描述:系统讲透 LeetCode 102 的层序 BFS、层宽控制与 DFS 深度分桶思路,并延伸到组织树、菜单树和波次执行等工程场景。 目标读者 想把 BFS 模板真正固定下来的 Hot100 刷题读者 会普通遍历,但一到“按层输出”就容易把层边界写乱的开发者 需要按深度分组展示树形结构的工程师 背景 / 动机 LeetCode 102 是树题里最标准的 BFS 入门题之一。 它训练的不是“遍历所有节点”,而是两件更重要的事: 如何用队列维护“下一批待处理节点” 如何准确切出“这一层”和“下一层”的边界 很多 BFS bug 都来自这里: 在遍历当前层时直接用不断变化的 queue.length 一边弹当前层,一边把新孩子混进当前层结果 忘记空树处理,导致访问空指针 把 102 的模板写稳,后面的: 右视图 每层平均值 锯齿层序遍历 最小深度 / 最大深度的 BFS 写法 都会自然很多。 ...
Hot100:对称二叉树(Symmetric Tree)镜像递归 / BFS ACERS 解析
副标题 / 摘要 对称二叉树的难点不在遍历,而在“比较方向”。你比较的不是左对左、右对右,而是镜像位置上的节点对。本文按 ACERS 结构拆解 LeetCode 101 的镜像递归合同、BFS 成对入队写法,以及工程中的对称结构校验场景。 预计阅读时长:10~12 分钟 标签:Hot100、二叉树、DFS、BFS、对称性 SEO 关键词:Hot100, Symmetric Tree, 对称二叉树, 镜像递归, BFS, LeetCode 101 元描述:系统讲透 LeetCode 101 的镜像递归与 BFS 对称校验思路,并延伸到布局树与拓扑模板的对称检查。 目标读者 刚从 100 相同的树过渡到“镜像比较”的刷题读者 会写普通树递归,但对“外侧 / 内侧”比较关系容易写乱的开发者 需要在布局树、模板树、镜像结构里做左右对称校验的工程师 背景 / 动机 LeetCode 101 很适合作为树题里的“方向感”训练: 你要先意识到,对称不是“左右子树完全一样” 它要求的是“左边看过去”和“右边镜像过来”之后一致 也就是说,比较方向从“同向”变成了“交叉” 很多人做这题时容易犯三类错误: 还沿用 100 的思路,写成 left.left 对 right.left 只比较节点值,不比较空节点位置 先翻转一棵子树再比较,结果多做了一轮变换,逻辑也更绕 这题真正训练的是“镜像递归模板”。掌握后,树对称、树镜像、结构匹配等题都会更清楚。 核心概念 镜像关系:左子树的左边,要和右子树的右边对应;左子树的右边,要和右子树的左边对应 外侧 / 内侧配对:left.left 对 right.right,left.right 对 right.left 成对递归:递归函数参数是两个节点,表示“这两个位置是否互为镜像” 成对入队:BFS 里队列保存的不是单个节点,而是需要一起比较的节点对 A — Algorithm(题目与算法) 题目还原 给你一个二叉树的根节点 root,检查它是否轴对称。 ...
相同的树(Same Tree)同步递归 / BFS ACERS 解析
副标题 / 摘要 LeetCode 100 的关键不在“会不会遍历树”,而在“能不能把两棵树当成一对一对的节点同步比较”。本文按 ACERS 结构拆解同步递归的判断合同、BFS 成对校验写法,以及工程里常见的结构等价判断场景。 预计阅读时长:9~11 分钟 标签:二叉树、DFS、BFS、树比较 SEO 关键词:Same Tree, 相同的树, 二叉树比较, 同步递归, LeetCode 100 元描述:系统讲透 LeetCode 100 的同步递归与 BFS 成对比较思路,并延伸到配置树、组件树和语法树的等价判断。 目标读者 刚开始刷树题,想建立“成对递归”思维的读者 能写单棵树 DFS,但一涉及“两棵树同时比较”就容易混乱的开发者 需要在配置树、组件树、语法树里判断结构是否一致的工程师 背景 / 动机 很多人第一次做 100,会本能地把问题理解成“分别遍历两棵树,再比较结果”。 这当然能做,但它绕远了。题目真正考的是: 你能不能把 p 和 q 上的对应节点同时拿出来看 你能不能把“相同”的定义拆成一套稳定的判断合同 你能不能在递归里先处理空节点,再处理值和子树 这类思维在后续很多树题里都会反复出现,比如: 判断一棵树是否是另一棵树的子树 判断左右子树是否镜像对称 校验两份树形配置是否结构等价 所以 100 虽然简单,但它是“双树同步递归模板”的起点。 核心概念 同步递归:递归函数参数不是一个节点,而是一对节点 p 和 q 结构相同:对应位置都存在节点,且左右子树结构也一致 值相同:对应位置的节点值相等 成对遍历:无论 DFS 还是 BFS,核心都是“每次处理一对节点” A — Algorithm(题目与算法) 题目还原 给你两棵二叉树的根节点 p 和 q,编写一个函数来检验这两棵树是否相同。 ...
Hot100:翻转二叉树(Invert Binary Tree)递归 / BFS ACERS 解析
副标题 / 摘要 翻转二叉树是一道看起来非常短、却能快速检验你是否真正理解递归结构的题。本文围绕 LeetCode 226 拆解“交换左右子树”的本质,给出递归 / BFS 两种做法,以及结构镜像在工程中的迁移思路。 预计阅读时长:8~10 分钟 标签:Hot100、二叉树、递归、BFS、树变换 SEO 关键词:Hot100, Invert Binary Tree, 翻转二叉树, 树镜像, 递归, LeetCode 226 元描述:讲清 LeetCode 226 的递归与 BFS 解法,并延伸到布局镜像、结构变换等工程场景。 目标读者 想检验自己是否真正理解“递归作用在整棵树每个节点上”的刷题读者 看到树题就下意识写遍历,但不确定该在什么时机处理当前节点的开发者 需要做树形结构镜像、布局翻转或对称转换的工程师 背景 / 动机 226 的代码通常很短,但它的思维非常典型: 当前节点要做什么? 把 left 和 right 交换。 子问题是什么? 左右子树本身也都要继续翻转。 这就是非常纯粹的“当前操作 + 递归处理子问题”。 如果你对这题没有完全吃透,往往会出现: 只交换根节点,不继续处理子树 交换后递归方向写乱 把本来能原地完成的事,额外重建一棵新树 核心概念 树镜像(mirror):把每个节点的左子树与右子树对调 原地变换(in-place transform):不新建整棵树,只交换指针 递归分治:当前节点处理完后,左右子树仍是同类型问题 BFS 层序变换:也可以按层把每个节点的左右孩子交换 A — Algorithm(题目与算法) 题目还原 给你一棵二叉树的根节点 root,请将这棵树翻转,并返回翻转后的根节点。 输入输出 名称 类型 描述 root TreeNode 二叉树根节点,可以为空 返回值 TreeNode 翻转后的根节点 示例 1 输入: root = [4,2,7,1,3,6,9] 输出: [4,7,2,9,6,3,1] 解释: 原树左右子树整体对调后,所有节点都完成镜像翻转。 示例 2 输入: root = [2,1,3] 输出: [2,3,1] 示例 3 输入: root = [] 输出: [] 约束 树中节点数目在 [0, 100] 内 -100 <= Node.val <= 100 C — Concepts(核心思想) 思路推导:为什么“交换 + 递归”就够了 假设当前节点是 node,我们要做的事情只有两步: ...