克隆图:哈希表 + 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 的节点。 ...

2026年3月19日 · 5 分钟 · map[name:Jeanphilo]

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 写法 都会自然很多。 ...

2026年3月15日 · 8 分钟 · map[name:Jeanphilo]

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,检查它是否轴对称。 ...

2026年3月15日 · 7 分钟 · map[name:Jeanphilo]

相同的树(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,编写一个函数来检验这两棵树是否相同。 ...

2026年3月15日 · 6 分钟 · map[name:Jeanphilo]

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,我们要做的事情只有两步: ...

2026年3月6日 · 6 分钟 · map[name:Jeanphilo]

Hot100:二叉树的最大深度(Maximum Depth of Binary Tree)DFS / BFS ACERS 解析

副标题 / 摘要 “最大深度”是树递归最标准的起手式。你只要真正理解“当前树的答案依赖左右子树答案”的定义,整类树形 DP / DFS 题都会顺很多。本文以 LeetCode 104 为核心,系统讲解递归 DFS、层序 BFS 与工程迁移方法。 预计阅读时长:9~11 分钟 标签:Hot100、二叉树、DFS、BFS、递归 SEO 关键词:Hot100, Maximum Depth of Binary Tree, 二叉树的最大深度, DFS, BFS, LeetCode 104 元描述:从深度定义出发,讲清 LeetCode 104 的 DFS 和 BFS 解法,并附多语言可运行代码。 目标读者 刚开始刷树题,想把“树递归返回值”真正吃透的同学 能写遍历,但一遇到“求高度 / 求路径 / 求答案”就容易混乱的开发者 需要在菜单树、组织架构、嵌套 JSON 等层级数据里做深度分析的工程师 背景 / 动机 LeetCode 104 看起来像一道“送分题”,但它几乎是所有树递归的母题: 你需要先回答“空树深度是多少” 再回答“当前节点的答案依赖谁” 最后把关系写成 1 + max(left, right) 一旦这个递归定义真正建立起来,后续的平衡二叉树、直径、路径和、最近公共祖先都会更容易进入状态。 核心概念 深度 / 高度:这里按题意,根到最远叶子节点的节点数 后序式思维:想知道当前节点答案,必须先知道左右子树答案 DFS:递归向下,回溯时组合答案 BFS:按层遍历,最后一层编号就是树深度 A — Algorithm(题目与算法) 题目还原 给定二叉树根节点 root,返回其 最大深度。 ...

2026年3月6日 · 5 分钟 · map[name:Jeanphilo]

Hot100:二叉树的中序遍历(Binary Tree Inorder Traversal)递归 / 显式栈 ACERS 解析

副标题 / 摘要 二叉树遍历是树题模板的起点,中序遍历则是“递归思维”和“显式栈模拟”最典型的一题。本文按 ACERS 结构拆解 LeetCode 94,把左-根-右的访问顺序、迭代栈写法和工程迁移价值一次讲清。 预计阅读时长:10~12 分钟 标签:Hot100、二叉树、DFS、栈、中序遍历 SEO 关键词:Hot100, Binary Tree Inorder Traversal, 二叉树的中序遍历, 中序遍历, 显式栈, LeetCode 94 元描述:从递归到显式栈,系统讲透 LeetCode 94 二叉树中序遍历,并给出工程场景迁移与多语言实现。 目标读者 正在刷 Hot100,希望把树遍历模板固定下来的同学 刚从数组 / 链表过渡到树结构,容易把前序、中序、后序顺序写混的开发者 需要在 BST、表达式树、抽象语法树里复用“左-根-右”思想的工程师 背景 / 动机 中序遍历本身不复杂,但它的训练价值很高: 它是“递归 = 隐式栈,迭代 = 显式栈”最容易建立直觉的一题 它能帮助你稳定掌握“先一路向左,再回退访问根,再转向右子树”的过程 在 二叉搜索树(BST) 里,中序遍历天然得到有序序列,工程迁移价值很强 很多人第一次写树题不是逻辑不会,而是: 不清楚访问顺序到底是谁先谁后 迭代版不知道什么时候入栈、什么时候出栈 一旦树为空或只有单边链,代码就容易写乱 这题把模板练熟,后面的验证 BST、找第 k 小元素、恢复二叉搜索树等题会更顺。 核心概念 中序遍历:按照 左子树 -> 根节点 -> 右子树 的顺序访问 DFS(深度优先搜索):树遍历最常见的组织方式,中序遍历就是 DFS 的一种访问顺序 显式栈:把递归调用栈手动写出来,用栈保存“回头还要处理的节点” 树高 h:空间复杂度通常写成 O(h),平衡树约为 O(log n),极端退化链表时是 O(n) A — Algorithm(题目与算法) 题目还原 给定二叉树根节点 root,返回它的 中序遍历 结果。 ...

2026年3月6日 · 6 分钟 · map[name:Jeanphilo]

LeetCode 146:LRU 缓存设计(O(1))哈希表 + 双向链表实战

副标题 / 摘要 这题不是“背答案题”,而是缓存系统的基本功:如何在常数时间内同时满足“快速访问”和“按最近最少使用淘汰”。本文从朴素方案推到最优结构,并给出可运行的多语言实现。 预计阅读时长:14~18 分钟 标签:LRU、哈希表、双向链表、系统设计 SEO 关键词:LRU Cache, LeetCode 146, 哈希表, 双向链表, O(1) 元描述:通过哈希表 + 双向链表实现 LRU 缓存,get/put 平均 O(1),附工程场景、常见坑与六语言实现。 A — Algorithm(题目与算法) 题目重述 设计并实现一个满足 LRU 约束的数据结构 LRUCache: LRUCache(int capacity):用正整数容量初始化 int get(int key):若 key 存在返回 value,否则返回 -1 void put(int key, int value): key 已存在:更新 value,并视作最近使用 key 不存在:插入新键值对 若超出容量:淘汰最久未使用的 key 并要求 get 和 put 平均时间复杂度为 O(1)。 示例 1(操作序列) LRUCache cache = new LRUCache(2) cache.put(1, 1) // 缓存: {1=1} cache.put(2, 2) // 缓存: {1=1, 2=2} cache.get(1) // 返回 1,且 1 变成最近使用 cache.put(3, 3) // 容量满,淘汰 key=2 cache.get(2) // 返回 -1 cache.put(4, 4) // 淘汰 key=1 cache.get(1) // 返回 -1 cache.get(3) // 返回 3 cache.get(4) // 返回 4 示例 2(更新已有键) LRUCache cache = new LRUCache(2) cache.put(1, 10) cache.put(1, 99) // 更新 value,且 1 视作最近使用 cache.get(1) // 返回 99 目标读者 正在刷 LeetCode 中等题、想吃透“数据结构组合技”的同学 做后端/中间件,需要实现或优化本地缓存的工程师 面试中经常被问到 LRU,但只记住结论、没掌握细节的人 背景 / 动机 缓存是“空间换时间”,但空间是有限的。 当缓存满了,必须淘汰一些键。LRU(Least Recently Used,最近最少使用)假设: ...

2026年2月11日 · 13 分钟 · map[name:Jeanphilo]

LeetCode 138:随机链表的复制(Copy List with Random Pointer)深拷贝全解析

副标题 / 摘要 这道题的难点不是遍历链表,而是正确复制 random 指针所形成的“跨节点引用关系”。本文从朴素思路推导到哈希映射法,讲清为什么它稳定、可维护、易工程落地。 预计阅读时长:12~16 分钟 标签:链表、深拷贝、哈希表、随机指针 SEO 关键词:LeetCode 138, Copy List with Random Pointer, 随机链表复制, 深拷贝, 哈希映射 元描述:用两趟遍历 + 映射表完成随机链表深拷贝,系统讲解正确性、复杂度、工程实践与六语言实现。 A — Algorithm(题目与算法) 题目重述 给定一个长度为 n 的链表,每个节点有: val next random(可指向任意节点或 null) 要求构造该链表的深拷贝并返回新头节点。 新链表中的任何指针都不能指向原链表节点。 输入 / 输出表示 题面常用 [val, random_index] 表示每个节点: val:节点值 random_index:random 指向的节点下标;若为空则为 null 你的函数入参只有 head,输出复制链表的头节点。 示例 1 输入: [[7,null],[13,0],[11,4],[10,2],[1,0]] 输出: [[7,null],[13,0],[11,4],[10,2],[1,0]] 解释: 输出与输入的“值与引用关系”一致,但节点是全新对象。 示例 2 输入: [[1,1],[2,1]] 输出: [[1,1],[2,1]] 解释: 第一个节点 random 指向第二个节点,第二个节点 random 指向自己。 目标读者 刷 LeetCode 时对 random 指针题目不够稳的开发者 想厘清“浅拷贝 vs 深拷贝”差异的同学 希望把算法思路迁移到工程对象复制场景的工程师 背景 / 动机 普通链表只要复制 val 和 next,逻辑很直观; 但随机链表多了一个 random 指针,它可能: ...

2026年2月11日 · 9 分钟 · map[name:Jeanphilo]

LeetCode 19:删除链表的倒数第 N 个结点(双指针一趟扫描)ACERS 全解析

副标题 / 摘要 这题的核心不是“删除节点”,而是“如何在单链表里定位倒数第 N 个节点的前驱”。本文从朴素思路推导到一趟双指针解法,用 ACERS 结构讲透正确性、边界处理与工程迁移。 预计阅读时长:12~15 分钟 适用场景标签:链表基础、双指针、面试高频 SEO 关键词:LeetCode 19, Remove Nth Node From End of List, 删除链表倒数第 N 个结点, 快慢指针, 哨兵节点 元描述(Meta Description):删除链表倒数第 N 个结点的完整 ACERS 解析:从暴力到一趟双指针,含复杂度、常见坑、工程示例与 Python/C/C++/Go/Rust/JS 代码。 A — Algorithm(题目与算法) 题目重述 给你一个链表,删除链表的倒数第 n 个结点,并返回链表的头结点。 输入输出 项目 类型 含义 head ListNode 单链表头结点 n int 倒数第 n 个位置 返回值 ListNode 删除目标节点后的头结点 示例 1 输入: head = [1,2,3,4,5], n = 2 输出: [1,2,3,5] 解释:倒数第 2 个节点是 4,删除后得到 [1,2,3,5]。 ...

2026年2月11日 · 11 分钟 · map[name:Jeanphilo]