Hot100 思维难点与注意事项
[!info] 目的 记录 Hot100 刷题中的思维卡点、易错点和复盘结论,方便后续回看和查漏补缺。
关联:[[Algorithm/_index|算法目录]]
记录模板
题目:``
- 日期:
- 难点类型:(状态定义 / 边界处理 / 数据结构选择 / 复杂度优化 / 代码实现)
- 卡住原因:
- 关键突破:
- 注意事项:
- 一句话复盘:
条目记录
哈希表类题目(两数之和 / 字母异位词分组 / 最长连续序列)
- 日期:2026-03-02
- 难点类型:数据结构选择 / 复杂度优化
- 核心共性:能用哈希把“重复查找、归类、存在性判断”从线性扫描降为常数级查找。
- 典型题目:
- 两数之和:用
值 -> 下标的映射,把配对查找优化到O(n)。 - 字母异位词分组:构造稳定且唯一的哈希键(排序串或字符计数)完成分组。
- 最长连续序列:用哈希集合做存在性判断,只从“序列起点”扩展,避免重复遍历。
- 两数之和:用
- 注意事项:
- 哈希题先想 key 的定义是否唯一、稳定、可快速计算。
- 两数之和一般先查后存,避免同一元素被重复使用。
- 最长连续序列避免从每个元素都向后遍历,否则会退化到
O(n^2)。
- 一句话复盘:哈希表题的本质是先建索引,再用常数时间查询或分组来换时间复杂度。
双指针类题目(移动零)
- 日期:2026-03-02
- 难点类型:指针语义 / 原地修改
- 核心共性:快指针负责扫描并找有效值,慢指针负责维护“已处理区间”的正确位置。
- 典型思路:
- 快指针遍历数组,遇到非
0元素就写到慢指针位置。 - 每次写入后慢指针后移一位。
- 快指针结束后,把慢指针到数组末尾的元素全部补为
0。
- 快指针遍历数组,遇到非
- 注意事项:
- 先覆盖非
0,再统一补0,逻辑最稳定。 - 这是原地算法,时间复杂度
O(n),额外空间复杂度O(1)。 - 不要在遍历时频繁交换或删除元素,容易引入额外复杂度和边界错误。
- 先覆盖非
- 一句话复盘:移动零的关键是职责分离,快指针找值,慢指针定位置。
双指针类题目(盛最多水的容器)
- 日期:2026-03-02
- 难点类型:贪心策略 / 指针移动规则
- 核心思路:左右指针从两端向中间逼近,每一步计算面积并维护最大值。
- 关键突破:每次移动较短的一侧,才有机会让最短边变大;移动较长边通常不会得到更优解。
- 注意事项:
- 面积公式:
(right - left) * min(height[left], height[right])。 - 遍历过程中持续更新最大面积即可,时间复杂度
O(n),空间复杂度O(1)。
- 面积公式:
- 一句话复盘:这题本质是“边收缩边取最优”,重点在于正确的指针移动策略。
双指针类题目(三数之和)
- 日期:2026-03-02
- 难点类型:去重策略 / 指针配合
- 核心思路:先固定第一个数,再用左右指针在剩余区间内逼近寻找另外两个数。
- 关键突破:这题正确性的关键不是只找到答案,而是系统地去重避免重复三元组。
- 注意事项:
- 先排序,再进行双指针,便于逼近和去重。
- 固定第一个数
i时,若nums[i] == nums[i-1]则跳过。 - 当
left + right与目标值配对成功后,左右指针都要跳过连续相同值,再继续移动。 - 时间复杂度
O(n^2),空间复杂度通常为O(1)(不计输出)。
- 一句话复盘:三数之和是“固定一个数 + 双指针”,难点在完整且不漏解的去重流程。
多解法题目(接雨水)
- 日期:2026-03-02
- 难点类型:状态定义 / 边界处理 / 数据结构选择
- 核心共性:每个位置可接雨水量由
min(左侧最高, 右侧最高) - 当前高度决定。 - 方案 1(前后缀最大值):
- 预处理
leftMax[i]和rightMax[i],再线性遍历累加每个位置雨水量。 - 时间复杂度
O(n),空间复杂度O(n)。
- 预处理
- 方案 2(双指针):
- 左右指针向中间收缩,同时维护
leftMax和rightMax。 - 每次由较低一侧确定当前可接雨水并移动该侧指针。
- 时间复杂度
O(n),空间复杂度O(1)。
- 左右指针向中间收缩,同时维护
- 方案 3(单调栈):
- 用单调递减栈保存下标,当前高度大于栈顶时弹栈并计算“凹槽”积水。
- 计算时要注意宽度为
right - left - 1,高度为min(height[left], height[right]) - height[bottom]。 - 时间复杂度
O(n),空间复杂度O(n)。
- 注意事项:
- 单调栈里建议存下标而不是高度,方便同时计算宽度和高度差。
- 双指针法的核心不是盲目比较两端值,而是基于当前
leftMax/rightMax判断哪一侧可确定。
- 一句话复盘:接雨水是一题多解,但本质都在求每个位置两侧“挡板下界”。
滑动窗口类题目(无重复字符的最长子串)
- 日期:2026-03-02
- 难点类型:窗口维护 / 指针移动规则
- 核心思路:可视作快慢指针。
fast负责扩张窗口,遇到重复字符时slow前移并逐步移出元素,直到窗口重新满足“无重复”。 - 关键突破:窗口始终保持“当前区间无重复”,每次扩张后更新最大长度。
- 注意事项:
- Go 中可用
map[byte]struct{}充当字符集合(若题目仅含 ASCII)。 - 删除窗口左端字符使用
delete(hash, s[slow]),再移动slow。 - 当字符串可能包含多字节字符时,需改用
rune处理,避免按字节切分出错。 - 时间复杂度
O(n),空间复杂度O(字符集大小)。
- Go 中可用
- 一句话复盘:这题本质是动态维护一个“合法窗口”,重复就缩窗,不重复就扩窗。
滑动窗口类题目(找到字符串中所有字母异位词)
- 日期:2026-03-02
- 难点类型:定长窗口 / 状态维护
- 核心思路:这是定长滑动窗口。窗口每右移一格,就执行“新字符计数 +1、旧字符计数 -1”,再判断当前窗口是否匹配。
- 关键突破:窗口长度固定为
len(p),所以不需要像可变窗口那样反复缩窗,按节奏推进即可。 - 实现体会:
- 可用
map做计数,也可用定长数组(如[26]int)做计数;你这题用数组是合理且常见的优化。 - 若“差值计数表”所有值都为
0,说明当前窗口与p字符频次一致,记录左边界索引。
- 可用
- 注意事项:
if i-len(p)+1 < 0 { continue }这段表示窗口还没形成完整长度,属于预热阶段,不进入通用匹配流程。- 记录答案时索引是窗口左端:
i-len(p)+1。 - 若字符集已知为小写字母,优先用数组计数,常数更小。
- 一句话复盘:这题本质是“固定长度窗口下的频次匹配”,关键在窗口预热与增减计数同步。
前缀和 + 哈希类题目(和为 K 的子数组)
- 日期:2026-03-02
- 难点类型:题型识别 / 状态转换
- 常见误判:容易先往滑动窗口上套,但这题数组元素可正可负,窗口和不具备单调性,无法稳定通过“扩窗/缩窗”判断方向。
- 核心思路:
- 设
pre[i]为前i个元素前缀和,则区间和满足:sum(l..r) = pre[r] - pre[l-1]。 - 遍历过程中用哈希表记录前缀和出现次数,查询当前
pre - k出现了多少次,即有多少个区间以当前位置结尾且和为k。 - 本质是用空间换时间,把区间和匹配从
O(n^2)降到O(n)。
- 设
- 注意事项:
- 初始化
count[0] = 1,表示“前缀和为 0 的空前缀”出现过一次。 - 统计顺序通常是先加答案
ans += count[pre-k],再更新count[pre]++,避免把当前前缀和提前用于自身匹配。 - 这里是“子数组”问题(连续区间),不是子串。
- 初始化
- 一句话复盘:和为
K的子数组要先想到前缀和差值,再用哈希表做快速计数。
滑动窗口类题目(滑动窗口最大值)
- 日期:2026-03-03
- 难点类型:数据结构选择 / 边界处理
- 核心思路:用单调结构维护窗口内可能成为最大值的元素,并在窗口滑动时及时移除过期下标。
- 关键突破:这题本质上要用单调双端队列(deque),不是普通单调栈;因为既要从尾部维护单调性,也要从头部弹出过期元素。
- 注意事项:
- 队列里通常存下标而不是值,便于判断是否超出窗口左边界。
- 入队前从队尾弹出所有小于等于当前值的下标,保证队列单调递减。
- 若队首下标
<= i-k,说明已离开窗口,需要弹出。 - 当
i >= k-1时,队首对应当前窗口最大值。 - 可用数组模拟双端队列完成上述操作。
- 一句话复盘:滑动窗口最大值的关键是“单调队列 + 过期下标淘汰”。
数组类题目(合并区间)
- 日期:2026-03-03
- 难点类型:排序后处理 / 区间边界维护
- 核心思路:先按区间左端点排序,再线性扫描合并重叠区间。
- 关键突破:排序后,后续区间只可能与当前合并段发生关系,处理逻辑会变成单次遍历。
- 注意事项:
- Go 中可用
slices.SortFunc自定义排序:slices.SortFunc(intervals, func(a, b []int) int { return a[0] - b[0] }) - 扫描时维护当前区间
[l, r]:- 若下一个区间左端点
<= r,则可合并并更新r = max(r, nextR)。 - 否则先把当前区间写入答案,再开启新区间。
- 若下一个区间左端点
- 时间复杂度
O(n log n)(排序主导),空间复杂度取决于结果集与实现方式。
- Go 中可用
- 一句话复盘:合并区间的本质是“先排序,再按边界关系合并”。
数组类题目(轮转数组)
- 日期:2026-03-03
- 难点类型:下标变换 / 原地操作
- 核心思路:三次翻转法。先整体翻转,再把前半段和后半段各翻转一次,即可完成右移轮转。
- 关键步骤:
- 先做
k %= n,统一轮转步数。 - 翻转整个数组。
- 翻转前
k个元素。 - 翻转后
n-k个元素。
- 先做
- 注意事项:
k可能大于数组长度,不取模会导致区间错误。- 特殊情况如
n == 0时要提前返回,避免取模异常。 - 时间复杂度
O(n),额外空间复杂度O(1)。
- 一句话复盘:轮转数组本质是“位置映射”,三次翻转是最稳的原地实现。
数组类题目(除自身以外数组的乘积)
- 日期:2026-03-03
- 难点类型:题意约束识别 / 前后缀构造
- 常见误判:先求整体乘积再除以当前元素,但会被
0元素和“不能使用除法”的限制卡住。 - 核心思路:用两段前缀乘积思想。
left[i]表示i左侧所有元素乘积。right[i]表示i右侧所有元素乘积。- 答案为
ans[i] = left[i] * right[i]。
- 注意事项:
- 可显式构造两个数组,也可先把左乘积写入
ans,再用一个变量从右往左累乘,做到O(1)额外空间(不计输出)。 - 边界初始化很关键:最左侧的左乘积和最右侧的右乘积都应为
1。 - 时间复杂度
O(n)。
- 可显式构造两个数组,也可先把左乘积写入
- 一句话复盘:这题关键是把“当前元素之外”拆成左半和右半,再做乘积合并。
数组类题目(缺失的第一个正数)
- 日期:2026-03-03
- 难点类型:复杂度约束识别 / 解法切换
- 常见误判:先想到排序,但排序是
O(n log n),不满足题目要求的O(n)时间复杂度。 - 核心思路:用哈希表记录出现过的正整数,再从
1开始递增检查第一个不存在的数。 - 注意事项:
- 只关心正数,非正数可以直接忽略。
- 哈希做法时间复杂度
O(n),空间复杂度O(n),属于用空间换时间。 - 这题还有进阶的原地做法(下标映射/原地哈希)可把额外空间降到
O(1)。
- 一句话复盘:先看清复杂度约束,再从排序思路切到哈希或原地哈希方案。
矩阵类题目(矩阵置零)
- 日期:2026-03-03
- 难点类型:状态标记 / 原地修改时机
- 核心思路:先遍历矩阵记录哪些行、哪些列需要置零,再二次遍历统一置零。
- 关键突破:把“发现 0”和“执行置 0”分两阶段,避免在遍历中提前改值污染后续判断。
- 注意事项:
- 用
rowZero[m]、colZero[n]分别标记目标行列。 - 第二次遍历时,只要
rowZero[i]或colZero[j]为真,就把matrix[i][j]设为0。 - 该解法时间复杂度
O(m*n),额外空间复杂度O(m+n)。 - 题目若要求常数空间,可进一步用首行首列当标记位优化。
- 用
- 一句话复盘:矩阵置零的核心是“先标记,后落地”,避免边改边判断。