- 教程
- 文档
【免费下载链接】LogicStack-LeetCode
公众号「宫水三叶的刷题日记」刷穿 LeetCode 系列文章源码
模拟(Simulation)是 LeetCode 中覆盖面最广、出现频率最高的题型之一:从字符串处理、矩阵遍历到游戏规则、状态机推演,凡是「按照题意把过程一步步走完」的题目,几乎都可以归类为模拟。本文以「宫水三叶的刷题日记」开源仓库 LogicStack-LeetCode 中的 Index/模拟.md 索引为骨架,系统梳理该仓库收录的 266+ 道模拟类题目,并结合仓库内多篇代表性题解源码,提炼出模拟题的通用识别方法、分类体系与实战套路,帮助你建立一套可复用的「模拟题做题框架」。
一、什么是模拟题:先学会「认出」它
模拟题的核心特征非常朴素:题目描述即算法。出题人把某个过程(一个游戏、一次遍历、一组规则、一段文本处理)用自然语言完整描述出来,解题者只需要忠实地把这个过程翻译成代码,而不需要引入贪心、DP 等额外的算法优化。
在 LogicStack-LeetCode 仓库中,每篇题解都会在开头用Tag : 「模拟」标注题型,例如 495. 提莫攻击(简单) 标注Tag : 「模拟」,1583. 统计不开心的朋友(中等) 则标注Tag : 「哈希表」、「模拟」——后者说明「模拟」常与哈希表、双指针、栈、队列等基础数据结构组合出现。
识别模拟题的三个典型信号:
| 信号 | 典型表现 | 示例题 |
|---|---|---|
| 过程化描述 | 题目给出完整步骤、回合、时间线 | 495. 提莫攻击、1503. 所有蚂蚁掉下来前的最后一刻 |
| 规则即约束 | 按给定的规则逐项判断、计数 | 412. Fizz Buzz、299. 猜数字游戏 |
| 状态推进 | 从初始状态出发,按规则一步步推进 | 38. 外观数列、1104. 二叉树寻路 |
二、读懂仓库的模拟题索引:结构与用法
Index/模拟.md是仓库中「模拟」这一主题的导航总表,采用四列表格组织全部收录题目:
- 题目列:链接到 LeetCode 原题,便于直接查看题目描述与提交评测;
- 题解列:链接到宫水三叶在 LeetCode 上的对应题解,涵盖完整思路推导与多语言代码;
- 难度列:简单 / 中等 / 困难,用于规划练习节奏;
- 推荐指数列:以 🤩 数量(1~5 个)标注推荐优先级,🤩🤩🤩🤩🤩 表示强烈推荐优先练习。
从索引可见,模拟题覆盖了从简单到困难的全难度梯度:
- 简单题(如 1. 两数之和、7. 整数反转、66. 加一、495. 提莫攻击)适合作为入门热身;
- 中难题(如 54. 螺旋矩阵、65. 有效数字、591. 标签验证器、1001. 网格照明)则要求更精细的状态设计,往往需要配合哈希表、栈、双指针等工具。
推荐的练习路径是:先刷推荐指数 ≥ 🤩🤩🤩🤩 的简单题建立手感,再挑战同指数的中难题,最后用困难题检验对「过程建模」的掌控力。
三、模拟题的通用方法论:来自源码的四步框架
对比仓库中多篇题解,可以提炼出模拟题的通用四步框架:
3.1 明确状态与哨兵值
模拟题的第一步是定义「需要维护的状态」。以 495. 提莫攻击 为例,核心状态是「上一次攻击的结束时间点last」。题解中特别强调了一个关键细节:last初始化为-1(哨兵值),而非0,目的是保证当timeSeries[0] = 0时第 0 秒也能被正常计数:
int ans = 0, last = -1; for (int s : timeSeries) { int e = s + duration - 1; ans += last < s ? duration : e - last; last = e; }这个哨兵设计的思路在模拟题中极其常见——边界状态(如第一次操作、空输入、起点为 0)往往需要专门处理,否则会产生经典的「差一错误」。
3.2 按区间/阶段拆分过程
当过程存在「重叠」或「分段」时,用区间视角拆分。提莫攻击题的核心洞见在于:若两次攻击不重合(last < s),本轮贡献完整的duration;若重合(last >= s),本轮只贡献增量e - last,相当于一次轻量级的区间合并。整体复杂度为 O(n) 时间、O(1) 空间。
3.3 建立索引/映射加速规则判断
当模拟过程需要频繁查询「两个对象的相对关系」时,预处理一张映射表是标准做法。以 1583. 统计不开心的朋友 为例:preferences[i]本身按亲密度从高到低排列,题解利用下标将其转换为「亲密度得分」map[i][preferences[i][j]] = n - j(得分越大越亲密),然后对每一对配对(x, y),遍历所有其他配对(u, v),检查是否存在「x 更喜欢 u 且 u 更喜欢 x」或「x 更喜欢 v 且 v 更喜欢 x」的互选背叛关系:
boolean check(int x, int y, int u, int v) { Map<Integer, Integer> xmap = map.get(x), umap = map.get(u); Map<Integer, Integer> vmap = map.get(v); if (xmap.get(u) > xmap.get(y) && umap.get(x) > umap.get(v)) return true; if (xmap.get(v) > xmap.get(y) && vmap.get(x) > vmap.get(u)) return true; return false; }题解同时给出两种实现:哈希表套哈希表(P1)与二维数组(P2),并指出在 n ≤ 500 的数据范围内二维数组充当哈希表完全可行——这提示我们:模拟题的存储结构选择应服从数据范围,不必一味追求复杂结构。
3.4 处理多语言实现的一致性
仓库题解几乎每篇都同时给出 Java / C++ / Python / TypeScript 实现,且各语言逻辑完全一致。例如外观数列题中「双指针求连续段」在四种语言中结构相同,仅语法差异。这意味着你可以把仓库当作「多语言对照表」:先看懂任意一种语言的思路,再对照翻译成自己熟悉或需要练习的语言。
四、按题材拆解:五类高频模拟题精讲
将索引中的 266+ 道题按题材归类,可以得到五类高频模拟场景,每类都有可复用的套路。
4.1 字符串模拟:逐字符 + 双指针
字符串模拟是数量最多的一类,代表题包括 38. 外观数列、65. 有效数字、468. 验证IP地址、71. 简化路径、8. 字符串转换整数 (atoi)(中等).md)。
以 38. 外观数列 为例,题解给出「从ans = "1"出发逐项递推,用双指针求连续段长度」的经典写法:
String ans = "1"; for (int i = 2; i <= n; i++) { String cur = ""; int m = ans.length(); for (int j = 0; j < m; ) { int k = j + 1; while (k < m && ans.charAt(j) == ans.charAt(k)) k++; int cnt = k - j; cur += cnt + "" + ans.charAt(j); j = k; } ans = cur; }其套路是:外层迭代阶段,内层双指针切分连续段,段内统计、段尾跳跃。这一模式同样适用于 443. 压缩字符串、1047. 删除字符串中的所有相邻重复项 等题。
4.2 矩阵模拟:按形状 / 按方向
矩阵类模拟题考察「坐标推进」能力,代表题有 54. 螺旋矩阵、59. 螺旋矩阵 II、867. 转置矩阵、1260. 二维网格迁移、1706. 球会落何处。
- 螺旋矩阵 的题解给出「按圈模拟」的思路:用左上角
(x1, y1)与右下角(x2, y2)框定当前圈,按「上边 → 右边 → 下边 → 左边」四条边遍历,随后两个角点同时向中心收缩一圈,递归执行;并处理两个退化情况——只有一行(x1 == x2)按行走、只有一列(y1 == y2)按列走。这种「形状驱动」的模拟比维护方向数组 + 转向标志的写法更直观、不易出错,是矩阵遍历题的优选模板。
另一种矩阵模拟思路是「状态驱动」,可参考 1706. 球会落何处:对每一列的球,根据当前格子的挡板方向与下一格子的挡板方向判断能否继续下落,模拟整条下落路径。
4.3 数学模拟:按公式 / 按周期
当过程背后存在可推导的数学规律时,模拟与数学推导常可互相转化。索引中 166. 分数到小数、168. Excel表列名称、171. Excel表列序号、400. 第 N 位数字、1185. 一周中的第几天 均属此类。典型技巧包括:取模定位周期、进制转换(如 Excel 列名的 26 进制映射)、长除法记录余数判断循环节等。
4.4 游戏 / 规则模拟:忠实翻译规则
游戏类模拟题强调「严格按规则逐回合推进」,代表题有 412. Fizz Buzz、682. 棒球比赛、1823. 找出游戏的获胜者、2069. 模拟行走机器人 II、794. 有效的井字游戏。这类题目的难点往往不在过程本身,而在于把自然语言规则翻译成精确的边界条件——建议先写伪代码列出所有规则分支,再落成代码。
4.5 数据结构模拟:选对容器
部分模拟题需要借助栈、队列、哈希表等容器来承载过程,代表题有 20. 有效的括号、150. 逆波兰表达式求值、385. 迷你语法分析器、636. 函数的独占时间、591. 标签验证器。容器选择的关键是匹配过程的访问模式:后进先出用栈、先进先出用队列、随机/频繁查询用哈希表或字典树。
五、从索引到源码:如何在本仓库高效刷模拟题
LogicStack-LeetCode 仓库的目录结构按题号区间组织(如LeetCode/491-500/、LeetCode/1701-1710/),每篇题解文件以「题号. 题名(难度).md」命名,与索引文档一一对应。推荐的刷题工作流:
- 按推荐指数筛选:在 Index/模拟.md 中按难度 + 🤩 数量挑选目标题,优先覆盖各题材的代表题;
- 先读题解正文:每篇题解均包含完整的题目描述、示例、提示(数据范围)、核心思路推导与多语言代码(Java / C++ / Python / TypeScript),并附时间复杂度与空间复杂度分析;
- 对照多语言实现:利用同题多语言代码对照理解,或直接摘抄为本地练习的起点;
- 按主题回看归类:索引同时关联了仓库中其他主题索引(如 Index/哈希表.md、Index/双指针.md、Index/栈.md),同一道题往往同时挂在多个主题下,刷完模拟主题后,可以顺藤摸瓜串起相关联的算法主题,形成知识网络。
六、总结
模拟题是算法学习中性价比极高的题材:它不要求先掌握高深的算法理论,却对「把自然语言翻译成精确代码」的能力提出扎实要求。借助 Index/模拟.md 这份覆盖 266+ 道题的索引地图,你可以按难度、按题材、按推荐指数系统练习;配合仓库内每篇题解的多语言源码与复杂度分析,既能快速入门,也能在「字符串双指针」「矩阵按圈遍历」「哈希映射预处理」等通用套路上建立肌肉记忆,为后续学习 DP、图论等更复杂的算法主题打下坚实的「忠实模拟」基础。
- 教程
- 文档
【免费下载链接】LogicStack-LeetCode
公众号「宫水三叶的刷题日记」刷穿 LeetCode 系列文章源码
相关推荐
LogicStack-LeetCode「构造」专题:23 道构造题的完整索引与源码级题解精讲
LogicStack LeetCode「构造」专题:23 道构造题的完整索引与源码级题解精讲 构造题是算法面试中非常特殊的一类:它不问你「能不能算出来」,而是问
教程文档codeforces-go 题解精讲:LeetCode 2729「有趣的数」双解法——位掩码模拟与打表
codeforces go 题解精讲:LeetCode 2729「有趣的数」双解法——位掩码模拟与打表 导读 本文以算法竞赛模板库 codeforces go
科学计算LogicStack-LeetCode 题解精讲:最长回文子串(LeetCode 5)——中心扩展与 Manacher 算法模板
LogicStack LeetCode 题解精讲:最长回文子串(LeetCode 5)——中心扩展与 Manacher 算法模板 本文是「宫水三叶的刷题日记」刷
教程文档
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考