news 2026/9/18 21:23:01

算法题总结274:从题解到可复用模式库的整理方法

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
算法题总结274:从题解到可复用模式库的整理方法

简介:这是一份面向技术面试和高频算法考察的总结性资料,整合了《剑指 offer》、LeetCode、LintCode 等主流题源中的典型问题,适合有基础、正在准备校招或跳槽的开发者集中突破。资源仅打包为 1 个 PDF 文件,大小 3.36MB,结构紧凑,方便在电脑或手机上随时翻阅。正文按数组、字符串、链表、树、栈和队列、数学、图、设计、海量数据、C/C++ 基础等模块展开,目录清晰,从数组中重复的数字、旋转数组的最小数字、连续子数组的最大和,到正则表达式匹配、最长公共子序列、二维数组中的查找等高频考点均有涉及。每道题给出题目来源和核心思路,既能用于快速过一遍常见题型,也能作为面试前的查漏补缺手册。目前已有 175 人学习,尤其适合需要系统梳理算法体系、提升解题效率的开发者。

1. 算法题总结 274:把刷过的题变成能复用的模式库

如果你手上有一份叫「算法题总结 274」的 PDF,不管它是从前辈那里拷来的题单,还是自己花了几个月整理的笔记,真正决定它价值的不是 274 这个数字,而是里面每道题有没有被拆解成可复用的模式。刷过几百道题的人常有这种体验:题量上去了,碰到新题还是不知道从哪下手;而有些人只刷了一百多道,却能在看到题目的前三十秒就锁定解法方向。差别不在记性,在总结方式。

最常见的错误是把总结做成题解合集:抄一遍代码、贴一段官方分析,然后就没有然后了。一份合格的算法题总结,本质上是一张模式检索表——看到「有序数组 + 查找」就想到二分查找算法;看到「子串匹配」就立刻画出 KMP 的 next 数组;看到「图上求最短路径」,先判断是单源还是多源,再决定是 Dijkstra 还是多源 BFS。本文以「算法题总结 274」这类题单为起点,讲怎么把散题整理成自己的模式库:先立分类框架,再沉淀高频算法模板,接着用一道题演示总结条目的写法,最后解决「总结完怎么复习」的问题。适合正在准备算法面试的工程师,也适合带竞赛、做内部技术分享的人参考。

2. 搭算法总结的框架:按数据结构与算法范式归档,而不是按题号归档

2.1 为什么按题号归档的题单留不下东西

多数 PDF 题单的目录是按题号排的,第 1 题到第 274 题依次排列。这种顺序对「从头做到尾」的刷题模式友好,但对复习极不友好:你记得第 148 题是道贪心算法题,但目录不会告诉你第 148、第 53、第 201 题其实是同一个套路。等刷到后半程,前面总结过的东西早就忘了,回翻又找不到对应的章节。

我一般会把总结文档的目录推翻重来,按「数据结构」和「算法范式」两个维度做标签,而不是按题目编号组织。这样做的理由很直接:面试和竞赛考的是模式识别能力,题目是模式的外壳,外壳可以千变万化,核心的状态转移、指针移动、堆的贪心选择是不变的。把同类题放在一起,才能看出规律;把不同类的题混在一起,只能复习到「我做过这道题」的错觉。

2.2 一套可落地的六段式分类骨架

下面这张表是我整理算法题总结时用的分类骨架,覆盖了绝大多数笔试和面试场景。你不必照搬,但建议至少包含「线性结构」「树与图」「动态规划」「经典算法范式」这四块,因为它们对应着面试官最常出题的方向。

大类子类典型题目特征需要记忆的核心结论
线性结构数组、链表、栈、队列、哈希表涉及遍历、双指针、单调栈、滑动窗口滑动窗口的扩展与收缩条件写在 while 里
树与图二叉树、二叉搜索树、图、拓扑树的遍历、最近公共祖先、最短路、连通分量树的递归返回值语义必须先定义清楚
动态规划线性 DP、区间 DP、背包、状态压缩存在重叠子问题和最优子结构dp 数组的下标含义是第一注释
经典范式排序、二分、贪心、回溯、剪枝有序数组查找、区间调度、排列组合枚举贪心需要证明,回溯需要画递归树
字符串KMP、Trie、Manacher、滚动哈希子串匹配、前缀查询、回文串next 数组是 KMP 的唯一难点
数学与杂项位运算、素数、快速幂、并查集与二进制、等价类、模运算相关并查集路径压缩必须配合按秩合并

做分类的时候有一个原则:一道题可以挂多个标签,但必须在其中一个标签下作为「主条目」存放完整总结,其他标签下只放一行引用。否则会出现同一道题在三个分类里各写一遍,改一处漏两处的情况。

2.3 用 Markdown 给每道题建一个固定结构的卡片

建立目录之后,每一道题都要有固定格式的卡片。我用 Markdown 写,因为可以放到 Git 仓库里做版本管理,也可以导出成 PDF 和 HTML。每个卡片包含七个字段:题目名与来源、难度、模式标签、一句话思路、复杂度、代码、踩坑记录。

## 题目:跳跃游戏 II - 标签:贪心算法 / 数组 / BFS思想 - 难度:中等 - 一句话思路:维护当前步能到达的最远位置,以及下一步能到达的最远位置 - 时间复杂度:O(n) - 空间复杂度:O(1) ### 代码(Python) def jump(nums): n = len(nums) # cur_end 是当前步的右边界,next_end 是下一步能到的最远位置 cur_end, next_end, steps = 0, 0, 0 for i in range(n - 1): next_end = max(next_end, i + nums[i]) if i == cur_end: cur_end = next_end steps += 1 return steps

字段说明:标签字段决定这道题在总目录里出现在哪几个小节,建议用「范式 + 数据结构」组合,比如「贪心算法 + 数组」;一句话思路必须是你自己重新组织过的语言,不能直接抄题解;复杂度写在代码之前,方便复习时先判断是否可能满足面试官要求。踩坑记录单独留一行,写「为什么这次没做出来」或「上次把边界条件写错在哪」。

提示:写「一句话思路」时有个检验标准——如果三个月后的你能靠这句话把代码默写出来,说明它合格;如果还需要看代码才能想起来,说明这句话写的是答案而不是思路。

3. 高频算法模板:总结里必须有的代码、边界与参数

3.1 二分查找模板:统一左闭右开写法

二分查找算法是面试中出现频率最高的基础算法之一,但也是边界条件出错率最高的地方。常见的错误是while (left <= right)while (left < right)混用,导致退出时leftright的关系不清晰。我建议在总结里只保留一种写法:左闭右开区间[left, right),把 mid 的计算和收缩条件固定下来。

# 在有序数组 nums 中查找 target,返回下标;不存在则返回 -1 def binary_search(nums, target): left, right = 0, len(nums) # right 是开区间边界 while left < right: mid = left + (right - left) // 2 # 防止整数溢出 if nums[mid] < target: left = mid + 1 # target 在右半区,mid 已经排除 else: right = mid # target 在左半区,mid 可能命中 # 退出循环时 left == right,只需验证这一个位置 return left if left < len(nums) and nums[left] == target else -1

为什么统一用左闭右开:区间[left, right)的不变量是「左闭右开」,所以当nums[mid] < target时,mid及其左边都必然小于 target,left = mid + 1是安全的;当nums[mid] >= target时,mid可能是答案,right = mid保留了 mid。循环退出时left == right,答案只可能在这个位置,省去同时考虑两个边界的麻烦。mid = left + (right - left) // 2是为了避免left + right在极端情况下溢出,这在 C++ 和 Java 里是必须写的防御式写法。

3.2 KMP 的 next 数组:错误匹配时回退的位置表

字符串匹配是算法题总结 274 这类题单里绕不开的题型。KMP 算法的核心不是匹配过程,而是 next 数组的构建——它是 KMP 与暴力匹配的唯一区别。next[i] 表示pattern[0:i](不含 i)这个前缀中,最长的相等真前后缀长度。构建 next 数组的代码要注意:i是当前要计算的位置,j是已经匹配的前缀长度。

// 构建 KMP 的 next 数组,pattern 是模式串 static int[] buildNext(String pattern) { int m = pattern.length(); int[] next = new int[m]; next[0] = 0; // 长度为 1 的前缀没有真前后缀 int j = 0; // j 表示已匹配的长度,也是下一次要比较的位置 for (int i = 1; i < m; i++) { // 失配时回退,直到匹配或回退到开头 while (j > 0 && pattern.charAt(i) != pattern.charAt(j)) { j = next[j - 1]; } if (pattern.charAt(i) == pattern.charAt(j)) { j++; } next[i] = j; } return next; }

这段代码里最容易迷惑的是while (j > 0 && pattern.charAt(i) != pattern.charAt(j))这一行。循环退出后有两种可能:j回退到 0,说明前面没有可以利用的相等前后缀;或者pattern.charAt(i) == pattern.charAt(j),说明找到了一个更短的相等前后缀。特别注意j = next[j - 1]而不是j--,因为回退的目标是「当前已匹配前缀的次长相等前后缀」,而不是简单往前挪一位。这是 KMP 从暴力匹配升级为线性的关键。

3.3 Dijkstra 与堆的参数:dist 数组的更新时机

图论算法里,Dijkstra 是单源最短路径的默认选择,前提是边权非负。用优先队列实现时,需要注意两个参数:优先队列里存的是「(当前距离, 节点编号)」,以及每次从堆顶取出节点时要判断该记录是否已经过期。过期判断是通过比较dist[v]curDist完成的。

// 邻接表存图,graph[u] = vector<pair<int, int>>,pair 为 (邻接点, 边权) vector<int> dijkstra(int n, vector<vector<pair<int, int>>>& graph, int src) { const int INF = INT_MAX / 2; vector<int> dist(n, INF); dist[src] = 0; // 优先队列,默认大顶堆,用 greater 转成小顶堆,按距离排序 priority_queue<pair<int, int>, vector<pair<int, int>>, greater<>> pq; pq.push({0, src}); while (!pq.empty()) { auto [curDist, u] = pq.top(); pq.pop(); if (curDist > dist[u]) continue; // 这条记录已过期,跳过 for (auto [v, w] : graph[u]) { if (dist[u] + w < dist[v]) { dist[v] = dist[u] + w; pq.push({dist[v], v}); } } } return dist; }

这里的关键参数是if (curDist > dist[u]) continue;这一行。由于同一个节点可能被多次加入优先队列,只有距离最短的那条记录才需要展开邻居;如果curDist大于当前已知最短距离,说明这是一条被后续更新超越的旧记录,直接丢弃。INF = INT_MAX / 2是为了防止dist[u] + w溢出,这是图论算法里最常见的隐蔽 bug。另外,普通 BFS 求最短路时用队列,Dijkstra 用优先队列,差别就在「弹出顺序」:BFS 按层序弹出,Dijkstra 按当前距离从小到大弹出。

3.4 排序与剪枝:什么时候排序,什么时候剪枝

排序算法本身是基础,但总结时更值得写的是「什么时候先排序」。常见做法是:题目涉及区间合并、求最大差值、贪心选择时,先排序把无序问题变成有序问题。归并排序、堆排序、快速排序各有适用场景——归并排序适合外部排序和求逆序对,堆排序适合数据流中取前 K 大,快速排序的平均性能最好但最坏情况退化到 O(n²)。

剪枝是回溯算法的优化手段,核心思路是在递归搜索树的节点上提前判断「继续往下走是否还有希望」。经典场景是组合求和:在进入下一层递归之前,判断当前累计值是否已经超过目标值,超过则直接跳过。另一个常用参数是「剪枝下界」:如果剩余元素全部加上也不够达到目标,也直接剪掉。这两类剪枝一个管上限,一个管下限,配合起来能把大部分无效递归挡在进入函数之前。

4. 实例:把一道题写成一份合格的总结条目

4.1 条目结构:题目信息、模式标签、复杂度、代码、变体

一份可以放进「算法题总结 274」PDF 的条目,至少应该有六块内容。题目信息和难度用来快速定位;模式标签是检索入口;一句话思路是给未来的自己看的;代码必须是可运行的完整版本,而不是片段;复杂度分析要写清楚时间复杂度和空间复杂度分别由哪一步产生;变体部分是拉分项,写出这道题改一个条件后会变成什么另一道题。

下面是一个条目模板的 Markdown 结构,可以直接复制进自己的总结文档:

# 跳跃游戏 II - 来源:LeetCode 45 / 面试高频 - 标签:贪心算法 / 数组 / 最少步数 - 一句话思路:每一步都贪心地扩展最远可达位置,步数只在到达当前步右边界时增加 ## 复杂度 - 时间:O(n),单次遍历 - 空间:O(1),只需要两个变量 ## 代码 (此处放可运行代码) ## 易错点 1. 循环条件是 i < n - 1,不是 i < n 2. 当 i 到达 cur_end 时才步数加一,而不是每次更新 next_end 都加 ## 变体 - 如果问能否到达终点:跳跃游戏 I,只维护最远可达位置 - 如果数组改为环形:需要先判断是否永远跳不出,再找起点

4.2 以最短路径和二分法做对比:总结时写什么

一份总结写的到底是「题解」还是「模式」,有一个非常直观的检验方法:看变体部分。以「跳跃游戏 II」这道贪心算法经典题为例,如果总结只写了代码和复杂度,那它撑不起「总结」二字;如果写了「当问题变成『最少步数』时,为什么贪心仍然成立」,读者就能把这道题的结论迁移到别的场景。

同样,二分查找算法的总结如果只写模板,那几乎所有二分题都长得一样。应该在总结里写明:什么时候用左闭右开,什么时候用左闭右闭,以及「找左边界」和「找右边界」分别是哪个分支要移动。很多面试题表面是「旋转数组找最小值」,实际上考的是二分边界条件的掌握程度。

我一般做法是:每做一道新题,先不看题解,用自己的话写「一句话思路」;写完代码后,再对照别人的解法补充「是否还有更优解法」;最后在变体栏里写一道与本题相似但条件不同的题。这个过程比刷三道新题拿到的提升更大,因为它在强迫你做模式匹配而不是机械重复。

4.3 常见总结误用:抄题解、不写失败点

整理算法题总结 274 这类 PDF 时,最常见的失败是《小抄型总结》——把题解代码直接复制进来,没有任何自己的思考痕迹。这种总结在整理的当下很有成就感,因为内容越来越多,但一个月后回翻,你会发现根本读不下去,因为代码旁边没有「为什么这么做」的注释。

第二个常见问题是不写失败点。我见过很多人的总结里只有标准解法,没有自己第一次提交时超时或越界的原因。建议每条总结至少保留一个「踩坑记录」字段——哪怕只是写「忘了处理空链表的极端情况」一行字,这行字在面试前翻一遍的价值远高于抄一段官方解析。面试官问你「这道题哪里容易错」,能答出失败点的人比能默写代码的人得分高得多。

提示:如果你真的只是想要一个可以快速翻阅的题单,那按题号整理 PDF 是够用的;但如果你想要的是面试前两个小时内能过完一遍的复习材料,必须按模式重新组织,且每个模式都有一句你自己的话。

5. 让 274 道题的总结长期可用:索引、复习节奏与自测

整理完的总结要能真正用起来,靠的不是意志力,而是索引和节奏。我建议做三件事:第一,在 PDF 的第一页放一张模式索引表,只保留「模式名 + 典型题ID + 一句话结论」,比如「二分查找 74/153/162:左闭右开,mid 用减法算」;第二,给每道题标记状态:green表示能独立写出,yellow表示需要提示,red表示完全不会,复习时只看红黄,绿色题过一眼思路就跳过;第三,给自己定一个固定的复习节奏——新题做完后第 1 天、第 3 天、第 7 天各回看一次同类题,之后每个周末把本周所有标红的题目重新默写一遍代码。

如果你用的是 Markdown 维护原文,可以写一个简单的脚本统计标红题目数量,观察下降趋势。下面这个 Python 片段按「标签 + 状态」统计总结文件里的题目分布:

import re with open("algo_notes.md", encoding="utf-8") as f: text = f.read() # 匹配形如 "- 状态:red" 的行,统计三种状态的数量 status_counts = { "red": len(re.findall(r"状态[::]\s*red", text)), "yellow": len(re.findall(r"状态[::]\s*yellow", text)), "green": len(re.findall(r"状态[::]\s*green", text)), } # 匹配形如 "- 标签:贪心算法 / 数组" 的行,按标签统计题量 tag_counter = {} for line in text.splitlines(): m = re.match(r"\s*-\s*标签[::]\s*(.+)", line) if m: for tag in m.group(1).split("/"): tag = tag.strip() tag_counter[tag] = tag_counter.get(tag, 0) + 1 print("状态分布:", status_counts) print("标签分布:", tag_counter)

配合一个简单的定时提醒:每周五下午跑一次这个脚本,如果red数量比上周多,说明这周的新题没有消化,需要减少刷题量、增加回看量。真正有效的刷题节奏不是「每天做 5 道新题」,而是「新题与复习保持 1:2 的比例」。你能在面试前快速过完 274 道题的核心结论,靠的是索引表和红黄绿状态,而不是从头到尾重新看一遍代码。把这两样沉淀到你的 PDF 里,这份总结才会从收藏夹里的文件变成你随时可以调用的能力。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/18 21:20:27

ValidX校验库集成指南:Maven与Gradle完整配置与排错技巧

做Java后端和Android开发的同学&#xff0c;最近多多少少应该都听过ValidX这个校验库。它和传统的JSR-303&#xff08;javax.validation&#xff09;用法完全不是一回事&#xff0c;不依赖一堆注解在实体类上东标西注&#xff0c;而是把校验逻辑收敛到链式API里&#xff0c;代码…

作者头像 李华
网站建设 2026/9/18 21:19:31

图像分辨率本质:PPI/DPI/PPCM与场景适配指南

1. 图像分辨率到底在说什么&#xff1a;不是像素越多越好&#xff0c;而是“匹配场景”才对你打开手机相册&#xff0c;随手点开一张照片&#xff0c;右上角弹出“57603240”&#xff0c;再点开微信里朋友发来的截图&#xff0c;显示“10801920”——这两个数字看起来差不多&am…

作者头像 李华
网站建设 2026/9/18 21:17:00

Ubuntu 20.04 软件中心与软件安装:apt/snap 恢复指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/18 21:16:12

系统提示词泄露与防护:system_prompts_leaks 实战解析

system_prompts_leaks 这个仓库标题&#xff0c;第一次在社区时间线上刷到时&#xff0c;我的第一反应不是看热闹&#xff0c;而是立刻回头翻了自家线上那套提示词&#xff0c;逐条检查有没有把不该写的东西写在里面。它做的事情说起来很朴素&#xff1a;把多个对话类 AI 产品背…

作者头像 李华
网站建设 2026/9/18 21:10:51

免费 Audition 替代品怎么选:3 步挑对免费音频编辑工具

免费 Audition 替代品怎么选&#xff1a;3 步挑对免费音频编辑工具 【免费下载链接】Adobe-Alternatives A list of alternatives for Adobe software 项目地址: https://gitcode.com/GitHub_Trending/ad/Adobe-Alternatives 每月又扣一次的 Audition 订阅费&#xff0c…

作者头像 李华