推荐阅读
- 先读高频题型概览与标准模板
- 再按分类刷题并记录可复用思路
- 最后复盘错题,形成个人题库
这里是 Hot100 系列的合集页。每题统一按 ACERS 模板写作,强调“题解模板化 + 工程场景迁移 + 多语言实现”。 推荐阅读 先从数组 / 哈希 / 前缀和等高频主题开始 每题掌握一个可复用的“方法模型” 做完题后再回到工程场景,强化迁移能力
题目要求 给定一个循环整数数组 nums,返回数组 answer。 对每个下标 i: answer[i] = 从 i 向右遇到的第一个严格大于 nums[i] 的元素值 如果绕行一圈后仍然没有更大元素,answer[i] = -1。 “循环数组”表示走过最后一个位置后,可以继续从下标 0 开始;但一个下标不能绕一圈后把自己当成答案。 LeetCode 提供的方法接口是: nextGreaterElements(nums: List[int]) -> List[int] 示例 1 输入:nums = [1,2,1] 输出:[2,-1,2] 第一个 1 向右首先遇到更大的 2。 2 绕行一圈也找不到严格更大的元素,答案是 -1。 最后一个 1 走到数组末尾后绕回开头,随后遇到 2。 示例 2 输入:nums = [2,2,2] 输出:[-1,-1,-1] 相等元素不属于“更大元素”。 示例 3 输入:nums = [7] 输出:[-1] 数组只有一个元素时,它不能把自己作为下一个更大元素。 约束 1 <= nums.length <= 10^4 -10^9 <= nums[i] <= 10^9 Step 1:循环数组中的“右边”到哪里结束 先看: ...
题目要求 给定一个正整数 n,返回它的二进制表示中 1 的个数。这个数量也叫 Hamming weight。 LeetCode 提供的方法接口是: hammingWeight(n: int) -> int 示例 1 输入:n = 11 二进制:1011 输出:3 示例 2 输入:n = 128 二进制:10000000 输出:1 约束 1 <= n <= 2^31 - 1 输入处于题目给定的非负整数范围内。 虽然当前约束从 1 开始,后面的实现也会自然处理 n = 0,并返回 0。 Step 1:先明确到底在数什么 先看一个不使用十进制表示的小任务: n = 101100₂ 从左到右可以看到三个 1: 1 0 1 1 0 0 ^ ^ ^ 所以答案是 3。 当前 baseline 是: 先看出整数的二进制表示,再人工统计其中的 1。 这个 baseline 的 break 是: ...
题目要求 题目给你一个无向图。这个图原本是一棵有 n 个节点的树,节点编号是 1..n,后来额外加了一条边。 树的定义是: 连通 没有环 加上一条额外边之后,图仍然连通,但会出现一个环。 现在给定边数组 edges,其中 edges[i] = [a, b] 表示节点 a 和节点 b 之间有一条无向边。题目要求返回一条可以删除的边,使剩下的图重新变成树。 如果有多个答案,返回在输入中最后出现的那条。 输入输出 输入:edges: List[List[int]] 输出:一条边 List[int] n == len(edges) 节点编号是 1..n 图中没有重复边 给定图是连通的 示例 输入:edges = [[1,2],[1,3],[2,3]] 输出:[2,3] 前两条边形成一棵树: 1 - 2 | 3 再加入 [2,3],2 和 3 之间已经能通过 2 -> 1 -> 3 连通。现在再加直接边,就形成环。 输入:edges = [[1,2],[2,3],[3,4],[1,4],[1,5]] 输出:[1,4] 加入 [1,4] 之前,1 和 4 已经能通过 1 -> 2 -> 3 -> 4 连通,所以 [1,4] 是冗余边。 ...
题目要求 输入输出 输入:整数 n 含义:爬到第 n 阶楼顶 每次可以爬 1 或 2 阶 输出:返回到达楼顶的不同走法数量 约束:1 <= n <= 45 示例 输入:n = 2 输出:2 解释:1+1,2 输入:n = 3 输出:3 解释:1+1+1,1+2,2+1 这篇只用 Python,从 dp[i] 的含义一步一步推出最终代码。 从 n = 3 的最后一步开始 先看最小能暴露转移的例子: n = 3 到第 3 阶的最后一步只可能来自: 第 2 阶,再走 1 阶 第 1 阶,再走 2 阶 所以“到第 3 阶的方法数”不是凭空算出来的,而是来自两个更小的位置。 Step 1:先定义更小的问题 直接问“到第 n 阶有几种走法”太大。先定义: dp[i] = 到达第 i 阶的方法数 注意这里的 i 是楼梯位置,不是数组下标含义上的第几个元素。 ...
题目要求 输入输出 输入:整数数组 cost cost[i] 表示踩到第 i 阶的代价 每次可以爬 1 或 2 阶 可以从下标 0 或下标 1 开始 输出:返回到达楼顶的最小花费 约束:2 <= cost.length <= 1000,0 <= cost[i] <= 999 示例 输入:cost = [10,15,20] 输出:15 解释:从下标 1 开始,付 15 后直接到达 top 输入:cost = [1,100,1,1,1,100,1,1,100,1] 输出:6 从 [10,15,20] 的 top 位置开始 先看最小例子: cost = [10,15,20] 楼顶不是下标 2,而是在最后一阶之后的位置,可以记为位置 3。 到达 top 位置 3 的最后一步只可能来自: 位置 2,付 cost[2] 位置 1,付 cost[1] 这题最容易错的地方就在这里:我们要求的是“到达 top 的花费”,不是“到达最后一个下标的花费”。 ...
副标题 / 摘要 216. 组合总和 III 给回溯再加了一条关键约束:不仅总和要命中,组合长度也必须恰好等于 k。这会把问题变成一个很干净的“固定长度组合搜索”。 预计阅读时长:12~15 分钟 标签:回溯、固定长度、剪枝、组合搜索 SEO 关键词:Combination Sum III, 组合总和 III, 固定长度回溯, k 个数, 剪枝, LeetCode 216 元描述:从题目本身推导 LeetCode 216 的稳定解法,真正理解固定长度 k、有界候选集 1..9 与只能使用一次的回溯模型。 A — Algorithm(题目与算法) 题目还原 找出所有满足条件的组合,使得: 从 1 到 9 中选数 一共恰好选 k 个数 这些数的和等于 n 每个数最多只能使用一次 返回所有可能的合法组合。 答案中不能包含重复组合,组合顺序不重要。 输入输出 名称 类型 描述 k int 需要选出的数字个数 n int 目标和 返回 int[][] 所有长度为 k 且和为 n 的合法组合 示例 1 输入:k = 3, n = 7 输出:[[1,2,4]] 示例 2 输入:k = 3, n = 9 输出:[[1,2,6],[1,3,5],[2,3,4]] 示例 3 输入:k = 4, n = 1 输出:[] 约束 2 <= k <= 9 1 <= n <= 60 目标读者 已经理解 39、40,现在准备继续叠加“长度必须恰好是 k”这个新约束的学习者 会写基本 DFS,但经常把“目标和”与“固定长度”混在一起判断的读者 想掌握固定长度组合搜索模板的开发者 背景 / 动机 这道题很适合作为 39 和 40 之后的下一题,因为它保留了回溯骨架,但搜索空间进一步收紧了: ...
副标题 / 摘要 如果说 39. 组合总和 教你“当前值还能继续用”,那 40. 组合总和 II 教你的就是下一层升级:数组里会出现重复值、每个位置只能用一次、去重必须发生在正确的树层上。 预计阅读时长:14~16 分钟 标签:回溯、去重、剪枝、组合搜索 SEO 关键词:Combination Sum II, 组合总和 II, 回溯, 同层去重, 剪枝, LeetCode 40 元描述:从题目本身一步一步推出 LeetCode 40 的稳定解法,真正理解排序、i + 1 递归和“同层去重”规则。 A — Algorithm(题目与算法) 题目还原 给定一个候选数组 candidates 和一个目标值 target, 找出所有和为 target 的不同组合,并以列表形式返回。 candidates 中的每个数字在每个组合里 最多只能使用一次。 最终答案中不能包含重复组合。 输入输出 名称 类型 描述 candidates int[] 候选数组,允许出现重复值 target int 目标和 返回 int[][] 所有和为 target 的不同组合 示例 1 输入:candidates = [10,1,2,7,6,1,5], target = 8 输出: [ [1,1,6], [1,2,5], [1,7], [2,6] ] 示例 2 输入:candidates = [2,5,2,1,2], target = 5 输出: [ [1,2,2], [5] ] 约束 1 <= candidates.length <= 100 1 <= candidates[i] <= 50 1 <= target <= 30 目标读者 已经做完 39. 组合总和,想继续搞清“不能重复选”之后模板怎么变的学习者 会写回溯框架,但一碰到输入里有重复值就容易写乱去重逻辑的读者 想真正掌握“同层去重”而不是硬背一句 if i > start and ... 的开发者 背景 / 动机 这道题是 39 的自然下一题,因为它同时改了两条规则: ...
副标题 / 摘要 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 的节点。 ...
副标题 / 摘要 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,编写一个函数来检验这两棵树是否相同。 ...
副标题 / 摘要 反转链表 II 的关键不在“会反转”,而在“只反转中间一段且不破坏两端连接”。本文用 ACERS 结构讲清哑节点定位、头插法重排与边界处理,给出可复用模板与多语言代码。 预计阅读时长:12~15 分钟 标签:链表、区间反转、哑节点 SEO 关键词:Reverse Linked List II, 反转链表 II, 区间反转, 哑节点, 头插法, LeetCode 92 元描述:单链表区间反转的工程化模板:哑节点 + 头插法,O(n)/O(1),附推导、常见坑与多语言实现。 目标读者 已会 206 反转链表,想进一步掌握“局部反转”的同学 经常在链表题里卡边界(left=1、right=n)的中级开发者 希望把链表指针操作做成稳定模板的工程师 背景 / 动机 Reverse Linked List(206)是“整条反转”,而 92 要求“只反转一个闭区间”。 这类“局部重排”在工程里非常常见: 任务链中的某个分段要逆序重放 事件日志只对一段做回滚重连 数据结构需要在不重建节点的前提下原地调整 难点并非复杂算法,而是: 找准区间前驱与区间首节点 反转过程中不丢失后续链路 区间反转后把前后两端重新接回去 核心概念 哑节点(dummy):统一处理 left = 1 场景,避免头节点特判地狱 前驱指针 prev:最终停在第 left-1 个节点(若 left=1 则停在 dummy) 当前指针 cur:初始为区间首节点 prev.next 头插法(head insertion):每次把 cur 后面的一个节点摘下,插到 prev 后面 A — Algorithm(题目与算法) 题目还原 给你单链表的头节点 head 和两个整数 left、right(1 <= left <= right <= n), 请你反转从位置 left 到位置 right 的链表节点,返回反转后的链表。 ...