Algorithm Notes · Reading Map

Hwcoder 算法笔记体系读书笔记

这份笔记把 Hwcoder 分类页中的算法笔记重新整理成一套可复习的知识地图:先用算法入门和 STL 建立语言工具,再按力扣题型提炼解题套路,最后把手撕深度学习算法接到 LLM 面试和工程实现上。

23篇源笔记结构化阅读
3条主线:基础、刷题、手撕
15篇力扣专题笔记
6篇深度学习手写专题

来源与整理原则

本页是读书笔记,不是外部页面全文镜像。整理时读取了分类页、站内搜索索引和站点地图,保留完整阅读清单、来源链接和知识结构;正文按自己的理解重写,重点提炼可迁移的解题模型、实现风险和复习顺序。

原始入口

hewei2001.pages.dev/categories/ 中的“算法笔记”分类,包含算法入门、力扣刷题和手撕经典算法三组内容。

索引规模

源站搜索索引中,算法相关条目合计约 212,929 字符、1,635 个代码块。这里压缩成学习地图,不逐段复刻。

整理目标

面向复习和面试:每个专题都回答“什么时候用、维护什么、不变量是什么、哪里最容易错”。

总学习地图:这套笔记应该按三层读

直接按源站发布时间或题号顺序读,会被细节淹没。更有效的读法是先搭工具层,再建立算法模式层,最后读手写模型组件。三层之间不是并列关系,而是从“会写 C++ 解题代码”逐步过渡到“能解释现代深度学习组件”。

第一层:语言与竞赛生存技能

对应《算法入门笔记 #1 杂记》和《算法入门笔记 #2 STL标准库》。这部分解决的是输入输出、调试、复杂度选择、容器 API、排序比较器和常见 C++ 坑。

复杂度 STL 调试 模板
第二层:力扣题型模式

对应数组、位运算、数据结构、二分分治、动态规划、图论、贪心、链表、数学、搜索、栈队列、字符串和树。重点不是背题,而是识别题目触发条件。

不变量 状态 边界 维护量
第三层:手撕深度学习组件

对应 Attention、神经网络基础、Transformer、损失函数与指标、经典机器学习、RLHF。这里的考点从“算对答案”转向“张量形状、数值稳定性和训练目标”。

Shape Loss Transformer RLHF

我的读法建议

不要把这套笔记当成“题解合集”。题解合集通常会让人记住单题技巧,但面试和工程测试更常考的是抽象能力:你能不能把一个新问题归约成已知维护结构,能不能说明为什么这个状态够用,能不能指出哪一行代码会在边界条件下炸掉。

阅读阶段 应该回答的问题 不应该陷入的误区
基础工具 这个容器的查询、插入、删除复杂度是多少?排序比较器是否满足严格弱序?输入规模对应什么复杂度上限? 把 STL 当黑盒背 API,忽略迭代器失效、哈希退化、比较函数写错导致的未定义行为。
刷题专题 题目中的单调性、局部维护量、子问题状态在哪里?有没有可以被前缀、栈、队列、堆、并查集压缩的信息? 看见题号就背答案,不做题型迁移,也不总结失败样例。
手写模型 每个张量维度是什么?训练和推理路径是否一致?损失函数是否数值稳定?cache、mask、归一化轴是否写对? 只记公式,不检查 batch/head/seq/hidden 轴,也不解释为什么实现要这么拆。

力扣专题精读:按“维护什么信息”重新分类

源站的 15 篇力扣专题覆盖了常见面试题型。下面不是逐题复述,而是把每组题背后的可迁移套路提炼出来。真正要记的是判断条件和不变量。

数组:哈希、前缀、差分、双指针和滑动窗口

数组题通常把暴力枚举的两层循环压成“当前元素 + 已见信息”。两数之和用哈希保存 complement,最长连续序列用集合跳过非起点,前缀和把区间和转成两个端点差,差分把区间加法转成边界事件。双指针和滑动窗口的本质是维护一个满足条件的连续区间。

关键判断:如果目标是“区间和/次数/余数”,优先想前缀;如果是“最长/最短连续子数组”,先问窗口条件是否可随右端点单调变化;如果是“三数/接雨水/盛水容器”,先找排序或左右边界上的单调性。

数位与二进制:用 bit 级贡献替代枚举

位运算题的核心是把整数看成 32 或 64 个独立维度。经典技巧包括 \(n & (n-1)\) 消掉最低位 1、异或的自反消元、按位统计贡献、用 mask 表示集合状态。很多看似数组题的“只出现一次”问题,本质都是利用异或或按位计数恢复信息。

关键判断:如果题目出现“每个数出现 k 次,只有一个例外”“汉明距离总和”“最小 XOR”“子数组按位与/或”,应优先按 bit 位拆贡献,而不是在整数空间里硬模拟。

数据结构:线段树、树状数组、并查集各管一件事

线段树维护区间聚合,适合区间查询和修改;树状数组更轻,适合前缀型可逆聚合;并查集维护动态连通分量,适合把“是否同组”压成根节点关系。读这组笔记时,应把每种结构的维护对象说清楚,而不是背实现长度。

关键判断:如果查询是区间最大值、区间和、动态更新,考虑线段树或 BIT;如果题目问连通性、等价关系、合并集合,考虑并查集;如果权值关系带比例,根节点路径上还要维护额外权重。

二分与分治:找“答案空间”而不是只找数组下标

二分查找不是模板题,而是证明答案空间存在单调谓词。旋转数组、二维矩阵、峰值、平方根都在考边界语义;二分答案则把“能不能做到 x”变成判定函数。分治部分包括最大子数组、快速选择、归并统计逆序对,核心是把全局目标拆成左右子问题和跨边界信息。

关键判断:当题目要求最小最大值、最大最小值、满足条件的临界点,并且检查某个候选值可在 \(O(n)\) 或 \(O(n\log n)\) 完成,二分答案通常成立。

动态规划:把“未来不会改变过去”的信息写进状态

一维 DP 负责线性序列和状态机;二维 DP 负责矩阵、字符串匹配、背包;复杂 DP 覆盖树形、区间、状压和数位。DP 的真正难点不是公式,而是状态是否足够且不过度。状态定义错了,后面的转移再漂亮也没用。

关键判断:看到“方案数、最大/最小代价、可行性、编辑距离、背包容量、买卖股票状态、树上选或不选”时,先写状态语义,再写转移来源,最后决定遍历顺序和初始化。

图论:节点访问状态比遍历代码更重要

图论笔记覆盖图遍历、矩阵图、拓扑排序、最短路、最小生成树和连通性。BFS/DFS 的差异不是递归和队列,而是搜索层次和状态回溯;拓扑排序维护入度为 0 的可执行节点;最短路维护已知最优距离;矩阵图则经常把坐标编码成节点。

关键判断:如果每条边权相等,用 BFS;非负边权用 Dijkstra;有依赖关系用拓扑;要连通性和合并用并查集;要“从多个源同时扩散”,多源 BFS 常常比逐点 BFS 更稳。

贪心:必须能讲清楚局部选择为什么不会后悔

贪心专题包括扫描贪心、置换环、单调队列和排序贪心。贪心不是“看起来选最大/最小”,而是需要交换论证、区间支配关系或单调不变量。比如跳跃游戏维护当前最远可达,划分字母区间维护字符最后出现位置,任务调度器维护频次约束。

关键判断:如果局部选择能被证明支配其他选择,或者排序后只需维护一个最优边界,贪心可能成立;如果选择会影响多个未来状态且不能压缩,通常要回到 DP。

链表:所有问题都在考指针生命周期

链表题覆盖扫描、合并、反转、双指针定位和缓存模拟。链表的难点不是算法思想,而是指针顺序:反转时先保存 next,删除时维护 prev,区间反转时处理 dummy 和尾连接。LRU/LFU 则把链表和哈希表组合起来实现 \(O(1)\) 访问和淘汰。

关键判断:删除头节点、反转头段、k 组翻转、环入口、相交点都应先放 dummy 或快慢指针框架,避免在头节点边界上写一堆特判。

数学:把总和换成每个元素的贡献

数学专题重点是乘法原理、组合数、素数筛、逆元、中位数不等式和拒绝采样。很多“所有子数组/子序列贡献之和”不能枚举对象,只能枚举元素,计算它作为最大值、最小值、唯一字符或边界的出现次数。

关键判断:如果题目要求所有子数组的某个统计量总和,优先问“每个元素会被统计多少次”;如果涉及取模除法,需要逆元;如果是绝对值距离和,先考虑中位数最优性。

搜索与剪枝:状态恢复比递归入口更重要

搜索回溯用于返回所有排列、组合、子集和括号生成;剪枝用于 N 皇后、删除无效括号等巨大搜索空间。回溯和普通 DFS 的差异在于状态会被修改并恢复。去重题的关键不是用不用 set,而是排序后在同层跳过重复分支。

关键判断:如果输出所有方案,指数复杂度通常不可避免,重点是剪枝;如果只求最短步数,BFS 往往更合适;如果有启发式下界,可考虑 A* 或 IDA*。

栈与队列:把“最近未解决元素”压住

栈队列专题覆盖模拟、括号配对、表达式和单调栈。单调栈维护的是还没找到右侧更大/更小边界的元素,适用于每日温度、柱状图最大矩形、子数组最小值之和。单调队列则维护滑动窗口内候选最优值。

关键判断:如果每个元素要找左/右第一个更大或更小,单调栈;如果窗口移动中要维护最大/最小,单调队列;如果存在嵌套结构和成对抵消,普通栈通常够用。

字符串与树:一个靠前缀结构,一个靠递归结构

字符串专题包括打印输出、KMP、Trie 和字符串哈希。树专题包括前中后序、层序、属性计算、路径问题和 LCA。字符串问题常通过前缀函数、字典树或 rolling hash 维护重复结构;树问题天然递归,常见套路是自底向上返回子树信息。

关键判断:字符串匹配优先问是否需要线性匹配、前缀共享或快速比较子串;树题优先问当前节点需要从左右子树拿什么信息,以及返回给父节点的值和全局答案是否相同。

通用模板:不要背代码,背不变量

下面四个模板不是为了替代源笔记中的代码,而是提炼出跨题型的“检查协议”。做题时只要协议对,代码可以短;协议错了,代码越长越危险。

二分答案协议

1. 定义答案空间 [lo, hi],确认答案一定在其中。
2. 定义 check(x):x 是否可行,且可行性随 x 单调变化。
3. 决定找 first true 还是 last true。
4. 每轮更新边界时保持答案不丢失。
5. 退出后用边界样例验证:最小值、最大值、无解或全可行。
读书笔记里的 insight

二分题最常见错误不是 while 写法,而是 check 没有单调性。先证明单调,再选模板。

动态规划协议

state: dp[i][...] 表示处理到什么位置、保留了什么必要历史。
transition: 当前状态从哪些更小状态转移而来。
order: 遍历顺序必须保证依赖状态已经算出。
init: 空集、边界、起点是否正确。
answer: 最终答案是某个状态、某一行/列,还是所有状态的最值。

一维线性 DP 的核心是“前一个状态够不够”;状态机 DP 的核心是“每个阶段的持仓/冷冻/可买等状态是否互斥且完备”;背包 DP 的核心是“容量维度倒序还是正序”;区间 DP 的核心是“小区间先于大区间”。

回溯协议

choose: 当前层有哪些候选动作。
commit: 修改 path、visited、剩余目标等状态。
prune: 在明显不可能成功时提前返回。
recurse: 进入下一层。
rollback: 恢复 commit 前状态,保证兄弟分支互不污染。

如果题目要求去重,优先考虑排序后在同一层跳过重复候选;如果题目要求组合而非排列,需要用 start 限制后续候选范围;如果题目要求括号、表达式或棋盘状态,剪枝条件通常比递归框架更重要。

单调结构协议

stack/deque 中存什么:值、下标,还是二者都要。
单调方向是什么:递增、递减,严格还是非严格。
弹出时结算什么:右边界、贡献、窗口过期、候选淘汰。
相等元素怎么处理:决定重复元素归属,避免重复计数。

子数组最小值之和、柱状图最大矩形、接雨水、滑动窗口最大值都能用这套协议解释。相等元素处理尤其重要,一侧严格一侧非严格,通常是为了把重复元素的贡献归属到唯一位置。

手撕经典算法:从题解能力切到工程解释能力

源站的 6 篇手撕算法笔记更接近 LLM/深度学习面试准备。它们和力扣题的区别是:力扣追求输出正确,手撕模型组件还要解释张量形状、数值稳定、训练推理差异和复杂度。

Attention 篇:SDPA、MHA、KV Cache、MQA/GQA/MLA

这篇的主线是从 \(\operatorname{softmax}(QK^\top/\sqrt{d_k})V\) 出发,逐步加入多头拆分、causal mask、KV cache 和 K/V head 共享。真正的考点是每一步的张量形状:(batch, heads, seq, head_dim) 如何拆、如何转置、如何合并,mask 如何广播到 score 矩阵。

复习重点:decode 阶段 q_len 常为 1,而 k_len 是历史长度加当前 token;MHA 的 KV cache 成本随 head 数增长,MQA/GQA 通过共享 K/V 降低 cache 压力。

神经网络篇:Norm、Dropout、反向传播和梯度累积

LayerNorm 在特征维度归一化,BatchNorm 在 batch 统计上归一化,RMSNorm 去掉均值中心化只保留均方根尺度。Dropout 需要区分训练和推理路径,梯度累积需要把多个 micro-batch 的梯度等价到一个大 batch 更新。

复习重点:归一化题必须先说清楚统计轴;梯度累积题必须说明 loss 是否除以 accumulation steps,否则有效学习率会被放大。

Transformer 篇:Embedding、RoPE、Encoder/Decoder 堆叠

Transformer 手写不只是把 Attention 和 FFN 拼起来,还要处理 token embedding、position embedding、RoPE、残差、归一化、encoder self-attention、decoder causal self-attention 和 cross-attention。Encoder 和 Decoder 的差异主要在 mask 和是否关注 encoder 输出。

复习重点:RoPE 不是简单加位置向量,而是对 Q/K 的成对维度做旋转;残差路径和 LayerNorm 的位置会影响 pre-norm/post-norm 稳定性。

经典函数篇:损失、激活和指标

这篇把 MSE、CE、BCE、KL、Focal,Sigmoid、Tanh、ReLU、GeLU、SwiGLU、Softmax,以及 PPL、ROUGE、BLEU 放在一起。读法是把每个函数拆成输入范围、输出语义、梯度特性和数值稳定实现。

复习重点:Softmax/CE 要用 log-sum-exp 稳定化;KL 要说明方向;Focal loss 通过降低易分类样本权重处理类别不平衡;PPL 本质是平均负对数似然的指数形式。

机器学习篇:线性、逻辑、Softmax 回归和 SGD

这篇规模相对小,但适合作为从传统 ML 过渡到深度学习的桥。线性回归对应平方误差和解析/梯度解;逻辑回归对应 sigmoid 和二分类 CE;Softmax 回归对应多分类 CE;SGD 则连接到小批量优化和反向传播。

复习重点:不要只写 forward,要能写出 loss 对 logits、权重和 bias 的梯度方向;同时能解释 L2 正则如何影响权重更新。

RLHF 篇:GAE、PPO、DPO、GRPO 和 KL

RLHF 篇把优势估计、PPO policy/value loss、DPO loss、GRPO loss、reward 归一化和无偏 KL 放在一起。它适合放在最后读,因为它需要同时理解概率、梯度、序列级 reward 和 reference model 约束。

复习重点:GAE 是在 bias 和 variance 间折中;PPO clipping 限制策略更新幅度;DPO 把偏好对转成 log-ratio 分类目标;GRPO 通过组内相对优势减少 critic 依赖。

手撕模型组件的统一检查表

检查项 应能说清楚的内容 常见失败方式
Shape batch、seq、hidden、head、head_dim 的位置,以及每次 transpose/view 后的形状。 (B, S, H)(B, heads, S, D) 混用,或者 transpose 后直接 view。
Mask padding mask 和 causal mask 的语义、形状和广播路径。 decode 单步仍使用完整方阵 mask,或把 True/False 语义写反。
Stability softmax、log、除法、方差、norm 中的数值稳定处理。 不用 log-sum-exp,不加 epsilon,半精度下溢或溢出。
Train/Eval Dropout、BatchNorm、cache、teacher forcing 在训练和推理阶段的差异。 推理时还启用 dropout,或训练路径误用 KV cache。
Objective loss 的优化对象、梯度流向和 reference/label/reward 的角色。 KL 方向说反,DPO 的 chosen/rejected log-ratio 写错,PPO advantage 未 detach。

复习路线:用 4 轮把笔记变成能力

这套内容不适合一次性读完。更高效的方法是每一轮只验证一种能力:第一轮看地图,第二轮补模板,第三轮做错题和边界,第四轮把手撕模型组件讲出来。

第 1 轮:结构扫读

目标是在 1 到 2 天内读完所有标题和小节,建立题型索引。每篇只记录触发条件,例如“区间和 -> 前缀”“所有子数组贡献 -> 单调栈/贡献法”“树路径 -> 后序返回值”。

第 2 轮:模板默写

只默写二分、DP、回溯、单调栈/队列、并查集、BFS/Dijkstra、链表反转、Trie、KMP、Attention forward 这些高复用骨架。默写后用边界样例打断。

第 3 轮:错题归因

不要只记录“不会做”。错因应归到状态定义、边界初始化、遍历顺序、重复计数、溢出、数据结构选错、复杂度误判、shape 错误或数值稳定问题。

第 4 轮:口述验收

每个专题用 2 分钟讲清楚:什么时候用、核心不变量、复杂度、一个典型坑。手撕模型组件还要讲 shape、训练推理差异和为什么这样实现。

一个更实用的验收标准

复习算法笔记时,不要用“看懂了”当标准。更可靠的标准是下面这些可以被验证的动作:

  1. 看到题目能在 60 秒内说出候选套路和放弃理由。
  2. 能写出至少一个暴力解,并解释如何压缩到目标复杂度。
  3. 能主动给出 3 个边界样例:空、最小、重复或极端值。
  4. 能说明数据结构维护的不是“元素”,而是某种必要信息。
  5. 手撕深度学习组件时,能在每一行前后标出张量形状。
最终 insight

算法学习的主线不是从简单题到困难题,而是从“显式枚举”走向“维护压缩后的充分信息”。哈希表、前缀和、单调栈、并查集、DP 状态、Attention 的 KV cache,本质都在回答同一个问题:我能不能只保留未来还需要的信息,其余全部丢掉。

源笔记清单

下面保留完整来源索引,便于回到原文查具体代码。日期来自源站 sitemap 中可解析到的 lastmod 字段。

源笔记 最后更新 本页阅读定位
算法入门笔记 #1 杂记2025-03-22竞赛输入输出、调试、复杂度、常见 C/C++ 坑。
算法入门笔记 #2 STL标准库2025-03-17C++ 容器、排序、比较器、常用库函数和 ACM 模板。
力扣刷题笔记 #01 数组2025-03-09哈希、前缀和、差分、离散化、双指针、滑动窗口。
力扣刷题笔记 #02 数位&二进制2025-02-23位技巧、按位贡献、异或消元、集合 mask。
力扣刷题笔记 #03 数据结构2023-03-01线段树、树状数组、并查集和带权连通关系。
力扣刷题笔记 #04 二分&分治2025-03-13二分查找、二分答案、快速选择、归并和逆序统计。
力扣刷题笔记 #05-1 一维动态规划2025-03-13线性 DP、股票状态机、打家劫舍和序列状态。
力扣刷题笔记 #05-2 二维动态规划2025-03-13矩阵 DP、字符串匹配、背包和编辑距离。
力扣刷题笔记 #05-3 复杂动态规划2023-02-02树形、区间、状压和数位 DP。
力扣刷题笔记 #06 图论2023-03-18BFS/DFS、矩阵图、拓扑排序、最短路和连通性。
力扣刷题笔记 #07 贪心算法2025-09-09扫描贪心、置换环、单调队列和排序贪心。
力扣刷题笔记 #08 链表2025-03-11链表扫描、合并、反转、快慢指针和缓存模拟。
力扣刷题笔记 #09 数学2024-07-14贡献法、组合数、逆元、素数筛、中位数和采样。
力扣刷题笔记 #10 搜索&剪枝2023-03-18回溯、剪枝、N 皇后、括号删除和启发式搜索。
力扣刷题笔记 #11 栈&队列2024-07-05栈模拟、括号匹配、表达式和单调栈。
力扣刷题笔记 #12 字符串2023-03-22输出模拟、KMP、Trie 和字符串哈希。
力扣刷题笔记 #13 树2023-03-18遍历、层序、树属性、路径问题和 LCA。
手撕经典算法 #1 Attention篇2025-03-20SDPA、MHA、KV Cache、MQA、GQA 和 MLA。
手撕经典算法 #2 神经网络篇2025-04-10LayerNorm、RMSNorm、BatchNorm、Dropout、反传和梯度累积。
手撕经典算法 #3 Transformer篇2025-03-18Embedding、RoPE、Encoder、Decoder 和完整 Transformer。
手撕经典算法 #4 经典函数篇2025-04-11损失函数、激活函数、PPL、ROUGE 和 BLEU。
手撕经典算法 #5 机器学习篇2025-05-04线性回归、逻辑回归、Softmax 回归、反向传播和 SGD。
手撕经典算法 #6 RLHF篇2025-05-04GAE、PPO、DPO、GRPO、reward 归一化和 KL。