
矩阵题在力扣 Hot 100 里的存在感算不上最强但几乎每隔几题就会撞上一个二维数组。我第一次刷的时候基本靠背模板螺旋矩阵背方向数组矩阵置零背标记法旋转图像背转置加翻转。结果刷完就忘隔两周再看还是不会。后来下定决心把这批矩阵题单独拎出来重走一轮才发现问题不在题目难而在没有把矩阵题当成一个完整专题去拆解。这篇就把我重新刷 Hot 100 矩阵题的全过程、思路分类、逐题要点和踩过的坑整理出来给准备系统刷题的人一条能直接照做的路线。这轮重走我给自己定的目标不是“做过一遍”而是“下次看到同一类题能在十分钟内定位思路”。所以我会把矩阵题分成操作模拟、搜索遍历、动态规划三大类每一类都抽出共同套路。适合的读者主要是刷完一轮但知识点散成一盘沙的人也适合准备面试前想快速把二维数组相关题目过一遍的选手。1. 为什么矩阵题值得单独拉出来刷1.1 矩阵题在 Hot 100 中的真实分布力扣 Hot 100 那 100 道题里表面上叫“矩阵题”的不算多严格按标签筛选也就几十道上下。但你要是细看很多题目只是包装不同二维数组里做动态规划本质就是矩阵路径二维网格里做岛屿计数本质就是对矩阵元素做状态遍历连二分查找都能套在“从左到右、从上到下都递增”的矩阵里。所以矩阵题真正的数量远比你想象的多并且覆盖了枚举、模拟、双指针、二分、DP、DFS、BFS、回溯几乎全部基础算法。从面试角度看矩阵题是“性价比非常高”的一类。链表题要背指针操作二叉树题要熟记递归套路动态规划题需要敏感的状态定义能力而矩阵题往往只需要你掌握几个固定思路。一旦把方向数组、边界收缩、原地标记、状态转移这几个基础动作练熟Hot 100 里的矩阵题大部分都能稳定拿下。这也是我建议把它单独重刷一次的原因题目之间共性极强适合成体系训练而不是靠零散记忆。1.2 一个容易被忽视的共性二维索引的边界焦虑几乎所有矩阵题的核心难点都是同一个——边界。一维数组你只需要盯着一个 index 别越界二维数组一下子变成两个 index 的联动还要考虑行优先还是列优先左上角右下角谁开谁闭。很多人在 LeetCode 上提交矩阵题WA 的原因不是算法错而是 for 循环的边界写错了一格。重走这轮我最大的感受是边界焦虑可以通过强制习惯来消除。不管写哪道题先问自己三个问题这个矩阵是空矩阵吗行和列可能不等长吗我当前操作的坐标范围是左闭右开还是全闭把这几个问题固定在解题第一步矩阵题的正确率会明显提升。这也是后面所有题目拆解里我会反复强调的东西。2. 矩阵题的整体分类与思维框架2.1 操作模拟类重点是原地与顺序操作模拟类指的就是旋转图像、螺旋矩阵、矩阵置零这种题目本身不要求你用多高深的算法而是考你能不能按规则把矩阵里的元素移动到位同时不覆盖掉还没有处理的数据。这类题最考验的是“用什么顺序去读写元素”。旋转图像如果直接按顺时针去搬元素很容易搬着搬着把原值覆盖掉螺旋矩阵如果不设计好边界收缩节奏走着走着就乱了矩阵置零如果老老实实拿额外数组记录做出来很简单但面试官通常会追问能不能做到 O(1) 空间。这类题的通用套路是先画图把某个元素的移动路径用箭头标出来标完你就会发现本质上是“若干个环”或“若干层矩形”在做轮换。我建议这类题不要硬记代码而是记移动模型旋转图像就是转置加镜像翻转螺旋矩阵就是从外层到内层一圈圈走矩阵置零就是用第一行第一列当标记位。模型记熟了就算面试时忘了模板也能现场推出来。2.2 搜索遍历类网格 DFS/BFS 的万能骨架搜索遍历类包括岛屿数量、腐烂的橘子、单词搜索这类题目。它们的共同点是矩阵里的每个格子是一个节点上下左右四个方向是边题目本质是让你在网格图上做遍历。这类题如果说有模板那就是方向数组加 visited 数组或者原地修改再套 DFS 或 BFS 的递归/队列框架。我把它单独归为一类是因为很多人第一次做岛屿数量时会很慌这不就是个二维数组吗怎么跟图论扯上关系了其实二维网格就是一种特殊形态的图每个格子的邻居最多四个。你把dfs(i, j)当成“访问当前格子并递归访问邻居”这个动作全题就通了。腐烂的橘子则是 BFS 的典型多源场景可以理解为多个腐烂橘子同时向外扩散用队列存下当前层就能算出扩散轮数。2.3 动态规划类矩阵天然适合做状态表动态规划类包括不同路径、最小路径和这类题目。这类题的特征是结果可以从左上角到右下角推导每个格子的状态只依赖左边和上边的格子。二维矩阵本身就是一个天然的状态转移表画出来比任何抽象解释都直观。我自己做题时会先在纸上画一个 3 乘 3 的小矩阵把状态值一格一格填进去填完基本就有了状态转移方程。这里要注意一个进阶方向很多矩阵 DP 题的空间复杂度可以优化到 O(n)因为当前行只依赖上一行。用滚动数组时下标计算容易写错我的习惯是先把二维写法写对确认能 AC 后再改滚动数组而不是一开始就追求最省空间的写法。毕竟面试里先给一个能跑的版本再主动优化比上来就写错要好得多。3. Hot 100 矩阵题逐题拆解与实操要点3.1 73 矩阵置零O(1) 空间的标记法这道题的要求是如果矩阵里某个元素是 0就把它所在的行和列全部置为 0且要求尽可能少用额外空间。最直接的想法是拿两个数组分别记录哪些行、哪些列需要置零代码大概十几行就能写完空间复杂度 O(mn)。但 Hot 100 的人数统计里很多人卡在进阶要求上能不能用 O(1) 空间O(1) 的做法我强烈建议自己推一遍。核心思路是用矩阵的第一行和第一列来充当那个标记数组先把所有“某行需要置零”和“某列需要置零”的信息写到第一列和第一行的对应位置然后根据标记去把剩余区域置零。这里最大的坑是第一行和第一列本身也可能本来就是 0会被标记干扰所以你需要先用两个额外变量单独记录“第一行是否有 0”和“第一列是否有 0”最后再处理这两条边。实际操作中我建议按这个顺序写先扫一遍矩阵把含 0 的行列信息落到第一行第一列再根据第一行第一列的标记从第二行第二列开始把对应行列置零最后处理第一行和第一列自己的置零。这个顺序反了就会把标记覆盖掉。我第一遍写的时候就是把“从第二行第二列开始处理”写成了“从第一行第一列开始”结果整道题全乱。记住标记位和被标记区域必须错开。3.2 54 螺旋矩阵方向数组与边界收缩螺旋矩阵要求按顺时针螺旋顺序返回矩阵中的所有元素。最容易想到的思路是模拟“小机器人走路”维护一个方向往前走走到边界或者已访问过的格子就右转。这个思路本身没错但我更推荐一种更好写的版本用上下左右四个边界变量 left、right、top、bottom每走完一条边就收缩对应边界。具体来说就是先从左到右遍历上边遍历完 top 加一然后从上到下遍历右边遍历完 right 减一再从右到左遍历下边遍历完 bottom 减一最后从下到上遍历左边遍历完 left 加一。循环条件写成 left right top bottom。这个写法的好处是你不需要记录哪些格子访问过也不用担心方向搞错每四步一个周期边界往内缩一圈非常机械反而不容易错。易错点有两个。第一空矩阵返回空数组但有些语言里matrix.length为 0 后访问matrix[0]会直接越界必须在开头就处理。第二当矩阵只剩一行或只剩一列时四条边中的某些边会重复所以每走完一条边要立刻检查循环条件是否还成立。我见过不少人没有做这一步导致同一行被重复读取进结果调试半天才发现是边界条件没及时更新的问题。3.3 48 旋转图像转置与翻转的组合拳旋转图像要求把 n 乘 n 的矩阵顺时针旋转 90 度而且必须在原地完成。这里有个非常重要的结论顺时针旋转 90 度等于先沿主对角线做转置再把每一行左右翻转。如果你不想记这个结论可以用四元素轮换法但代码写起来会稍微长一点还需要搞清楚下标映射容易烦躁。我推荐的做法是两轮循环。第一轮把matrix[i][j]和matrix[j][i]交换即完成转置第二轮对每一行做双指针翻转把matrix[i][j]和matrix[i][n-1-j]交换。这样每一步都很直观测试用例也好画。我当时在纸上画了 3 乘 3 的矩阵推导发现转置加翻转就是顺时针旋转突然就觉得这类题不再需要背模板了。说到下标映射如果非要手推四元素轮转规律也简单当前位置(i, j)旋转后到(j, n-1-i)。但四个元素同时换你得先把其中一个值存到临时变量然后逆时针方向逐个覆盖。我的建议是除非你能在五分钟内推出来否则老老实实用转置加翻转不容易出错代码也短。很多大厂面试写题时间紧凑用最靠谱的方法比用最炫的方法更划算。3.4 240 搜索二维矩阵 II从右上角出发的 Z 字形查找这道题给的矩阵有两个特性每行从左到右递增每列从上到下递增。暴力遍历能过但显然不是面试官想要的。最优解法复杂度是 O(mn)思路是选一个特殊起点让每次比较都能排除一整行或一整列。我选的是右上角或者左下角看个人习惯。从右上角出发当前值如果等于 target 直接返回如果当前值大于 target说明当前列从上到下都大于 target因为下方元素更大就把列号减一如果当前值小于 target说明当前行从左到右都比 target 小就把行号加一。每次移动排除一行或一列所以最坏情况走完 m 行 n 列复杂度 O(mn)。这个题很多人第一次会想到从左上角出发二分但左上角是矩阵最小值右下角是矩阵最大值从它们出发都判断不了该往哪走。我自己的教训是解题前一定要画一个矩阵把“当前点在哪、能排除哪个方向”标出来。从左上角和右下角出发看似自然实际是个死胡同。从右上角或左下角出发才能利用好“一边大一边小”的性质。3.5 62 不同路径和 64 最小路径和一箭双雕的 DP 基础题这两道题放在一起说因为它们的代码结构几乎一样。不同路径是求从左上角到右下角有多少条路径最小路径和是求从左上角到右下角路径上数字之和最小。状态转移方程都是dp[i][j] 依赖 dp[i-1][j] 和 dp[i][j-1]区别只在转移公式是加法还是取最小值。不同路径里有个非常经典的边界处理第一行和第一列的dp值都应该是 1因为从起点只能一直向右或一直向下走到这些位置。最小路径和里第一行和第一列不是 1而是前缀和因为路径只能沿着边走所以要把前一个格子的累加值传下来。很多人在这个初始化上栽跟头尤其是忘记第一列也要初始化导致答案偏小。两道题我建议都用滚动数组优化写一遍。不同路径用一维dp数组时dp[j]在更新前代表的是上一行的值更新后代表当前行的值理解这个“覆盖前先读”的过程比背代码重要。最小路径和的滚动数组本质同理但注意更新顺序要从左到右因为需要用到当前行左边的更新结果。如果你只做一遍二维 DP建议第二遍强制自己换成滚动数组面试时被问空间复杂度优化就能从容应对。3.6 79 单词搜索网格回溯的剪枝要点单词搜索是在二维网格中找是否存在一条路径能拼出给定单词。路径可以上下左右走同一个格子不能重复使用。这类题是标准的网格 DFS 加回溯从每个格子出发尝试匹配第一个字符然后递归匹配下一个字符同时维护一个 visited 集合防止重复走。我的实现习惯是把 visited 直接改在原矩阵上比如访问过的临时改成#递归返回时再改回来。这样做省去额外数组但必须注意回溯必须还原否则会影响其他分支的搜索。剪枝上有个很有效的技巧提前检查第一个字符和最后一个字符是否在矩阵中出现过如果没有直接返回 false统计每个字符出现次数如果单词中某个字符出现次数比矩阵里多也不必搜。这道题最容易超时的地方是没加剪枝。我第一版代码在一个大用例上跑了 4 秒多加上“首尾字符判断”和“字符计数”剪枝后降到 20 毫秒左右。从这个题开始我养成了写完回溯先测边界用例的习惯尤其是“word 长度为 1”和“所有格子都是同一个字符”这类极端输入。3.7 200 岛屿数量和 994 腐烂的橘子网格图遍历双雄岛屿数量问矩阵中有多少个由 1 组成的连通块腐烂的橘子问多久能感染全部新鲜橘子。前者最适合 DFS遍历每个格子遇到 1 就计数器加一并把这个连通块的所有 1 都改成 0避免重复计数。后者最适合 BFS因为要记录扩散几轮把所有初始腐烂橘子入队按层扩散。岛屿数量有个很关键的点直接修改输入矩阵是允许的把访问过的陆地改成 0比维护 visited 数组省事得多。面试时如果面试官问“如果不想修改原数组怎么办”你再改用一个 visited 数组。腐烂橘子的 BFS 层数统计有个细节你需要记录队列的当前长度一次性处理完当前层的所有节点轮数才准确。如果你每出队一个节点就加一轮得到的是节点数量而不是轮数。这两个题写熟之后网格 DFS/BFS 你基本就掌握了。后面遇到像被围绕的区域、太平洋大西洋水流问题这类更复杂的网格题套路完全一致只是额外增加一些判断条件。3.8 从矩阵快速幂延伸出的思考Hot 100 里并没有一道题直接要求用矩阵快速幂但热词榜上围绕矩阵快速幂的搜索热度一直不低因为求斐波那契数列的高级解法就是构造转移矩阵再做快速幂。如果你追求的是完整掌握矩阵相关技巧我建议在刷完上面这些题之后花半天时间把快速幂和矩阵乘法结合起来。矩阵快速幂的代码模板不算长核心是把整数快速幂的“乘”换成“矩阵乘法”单位 1 换成单位矩阵 I。难点在矩阵乘法的三重循环别写错下标。我自己的记忆口诀是结果矩阵的(i, j)等于左矩阵第 i 行和右矩阵第 j 列逐项相乘再求和。练一题就够推荐选 70 爬楼梯用矩阵快速幂解、或者直接拿斐波那契数列练手能感受到矩阵到底是怎么加速递推的。4. 实操过程我的三轮刷题路线与复盘方法4.1 第一轮按矩阵题的标签分类刷第一轮我不按 Hot 100 编号顺序刷而是按标签刷。先把 73、54、48、240 这四道纯操作模拟题放一起一天之内连刷第二天把 62、64 这类动态规划放一起第三天集中写 79、200、994 这类搜索题。这样做的原因是同类题连在一起做大脑会自动提炼共同点记忆效率远高于每天换一个标签刷。具体到每天的时间分配我一般是上午画图推导思路下午动手写代码晚上把当天题目的思路用两句话写在专门的刷题笔记里。刷题笔记只记三样核心思路、时间空间复杂度、踩坑点。比如旋转图像的那条我的笔记只写了“转置 左右翻转注意交换两次等于没换”。三个月后再翻几秒钟就能想起来。4.2 第二轮按解法维度交叉刷第二轮我换了个维度不再按题目标签而是按解法维度交叉刷。比如把旋转图像、转置矩阵这类操作题和矩阵置零放一起专门研究“原地算法”这个主题把不同路径、最小路径和和搜索二维矩阵放一起研究“从左上角到右下角”的各种模型把岛屿数量、腐烂的橘子、单词搜索放一起研究方向数组的通用写法。交叉刷的好处是打破惯性思维。第一轮你可能已经记住了“旋转图像 先转置再翻转”但第二轮把这题放在“原地操作”主题下你会开始思考为什么不能先翻转再转置顺时针和逆时针有什么区别这样跳出题目本身才能真正理解解法背后的原理。我二轮刷完明显感觉举一反三的能力提升了一截。4.3 第三轮限时模拟与错题重做第三轮模拟面试节奏每道题限时 25 到 30 分钟。到了这个阶段如果某些题还是卡住我不会立刻看题解而是先看自己的刷题笔记回忆当时的思路再看是哪里断了。如果看笔记也接不上就说明这道题你之前根本没学透需要重新推一遍而不是简单把答案背下来。我给自己定了一个规则连续两周每周重做一次所有矩阵题凡是能不看题解、不查笔记、一次 AC 的题从错题本里移出任何一次卡壳的题重新回到第一轮的笔记里划重点。这个“滚动淘汰制”虽然费时间但效果很好重走一轮后Hot 100 里的矩阵题我基本能稳定在 20 分钟内 AC。4.4 刷题环境与工具上的小建议刷题环境方面我用的是 Python 写 LeetCode因为代码短适合快速验证思路。但要注意Python 列表推导虽然方便在矩阵题里反而容易写出可读性差的代码我建议能写普通循环就写普通循环。比如要创建 m 行 n 列的二维数组[[0] * n for _ in range(m)]和[[0] * n] * m结果截然不同后者每一行是同一个对象的引用改一格会带崩整列这个坑几乎每个 Python 刷题的人都踩过。我还习惯写完后把矩阵打印出来调试特别是在二维 DP 和 DFS 回溯的题目里。print大法虽然土但直观。比如最小路径和的 DP 表打印出来人工检查一遍数字比单看是否 AC 更能加深理解。等到你熟练了再把 print 去掉提交前记得删掉调试代码就行。5. 常见问题与排查技巧实录5.1 高频报错之一索引越界矩阵题一半的报错都是 index out of range。常见原因不外乎三种空矩阵没处理、方向数组四个方向里某个方向导致坐标出界、BFS 队列里的节点坐标没有先检查再访问。排查技巧是在你访问matrix[i][j]之前先问一句i 和 j 是否一定在合法范围内如果用了方向数组循环每一轮都要重新计算上下左右四个新坐标再对每个新坐标做边界检查。我自己的习惯是写一个私有方法in_area(i, j)专门做边界判断返回一个布尔值。这样代码读起来很清晰也不容易漏判。哪怕多写几个 if也比因为越界反复提交省时间。5.2 高频报错之二原地修改导致数据被覆盖矩阵置零、旋转图像这类题特别容易在“原地”两个字上翻车。矩阵置零里如果你先把某个 0 所在的行整行置零后面再遍历到这一行的其他列时你已经不知道这里原本是不是 0 了。旋转图像更明显四个元素轮转时如果不先存一个临时值第一次覆盖就把原值丢了。排查思路是把矩阵每个元素当成一个需要保护的“原值”任何修改前先画清楚依赖关系。我强烈建议这类题在草稿纸上写几个具体坐标比如(0, 0)、(0, n-1)、(n-1, n-1)手动走一遍赋值顺序再上代码。纸上能走通代码就不会错。5.3 高频报错之三DFS/BFS 死循环或超时网格搜索类题最怕死循环常见原因是访问过的节点没有打标记导致两个格子互相访问。比如从 A 走到 B又从 B 走回 A如果 A 没有标记成已访问就会无限递归。所以每次进入 DFS 函数的第一步就是立刻把当前格子标记为已访问。超时的另一个常见原因是剪枝不足。单词搜索如果完全不做预处理遇到大矩阵长单词会非常慢。排查顺序是先排除最简单的情况比如首尾字符不在矩阵里再考虑每次递归能不能提前终止比如已经匹配完所有字符就立即返回。不要小看这些“脏活累活”它们是区分能 AC 和超时的关键。5.4 一个关于调试的小技巧小矩阵手工推演矩阵题的调试我强烈推荐用 2 乘 3、3 乘 3 这种小矩阵手工推演。比如测试旋转图像就用[[1,2,3],[4,5,6],[7,8,9]]手动写出旋转后的结果[[7,4,1],[8,5,2],[9,6,3]]然后盯住代码中间过程。只要一个小矩阵能跑对算法本身大概率没问题出错往往是循环边界写错。小矩阵推演比看错误信息快得多尤其是 DP 表和回溯路径这种可视化问题。6. 从刷题到工程应用矩阵操作不只是考试题目6.1 图像处理中的矩阵操作旋转、翻转、转置这些操作放在工程师日常场景里其实就是图像处理的基本动作。图片在计算机里就是以像素矩阵存储的90 度旋转就是旋转图像那道题的代码水平翻转就是在转置基础上的每一行逆序。灰度图是一个二维矩阵彩色图则多了一个通道维度。所以理解矩阵操作的底层逻辑对写图像处理相关的代码非常有帮助。我在实际项目里做过一个类似功能把某个传感器的数据按二维网格组织然后需要整体旋转 90 度展示给前端绕来绕去发现就是力扣 48 的代码。这一刻我才真正觉得刷题不是刷了个寂寞矩阵题的这些基本功是会迁移到真实开发里的。6.2 动态规划表与状态矩阵不同路径、最小路径和这种二维 DP本质上是在构建一张状态矩阵。很多算法问题比如编辑距离、最长公共子序列、背包问题最终都会变成填一张 DP 表。你如果不熟悉二维矩阵的填表顺序就会困惑为什么dp[i][j]要依赖左边和上边的格子。建议在刷完 62 和 64 之后顺手再看一眼编辑距离那道经典题你会发现结构惊人地相似都是矩阵 DP 的变体。一旦建立起“DP 表就是一张矩阵”的心智模型刷题和工程里的动态规划会顺手很多。6.3 更深的数学扩展特征值分解与矩阵快速幂热词里出现了矩阵特征值分解和矩阵求导这类偏数学的内容这些更多属于矩阵论而不是力扣刷题的主流范畴。但如果你对数学感兴趣可以了解一个方向矩阵的特征值分解本质上是找到一组基让矩阵在这个基下表现为对角阵。这和动态规划的状态压缩、马尔可夫链的平稳分布都有关系属于刷题之外的长期积累不需要为了面试死磕。矩阵快速幂倒是和算法题直接相关斐波那契、线性递推都能用矩阵快速幂加速到 O(log n)。学有余力时值得一看但不建议放在矩阵专题的开头刷容易劝退。先把基础题吃透再扩展这些高级用法节奏更合理。7. 重走一轮之后的几个真实心得先说说记忆强度的问题。我第一轮刷矩阵题平均每道题记住的时间不超过一周原因是我在背代码而不是在记模型。这轮重走我把每一道题的解法压缩成一个画面旋转图像先想到转置再翻转螺旋矩阵先想到四个边界往内收搜索二维矩阵先想到右上角当起点。这些画面比代码容易记得多而且只要画面在代码完全可以现场推。我建议你也试一下这个方法把题目和画面绑定而不是和代码绑定。再说说口算复杂度的习惯。很多题解会直接告诉你时间是 O(mn) 还是 O(mn)但如果你自己也推一遍理解会更深。比如螺旋矩阵虽然看起来循环很多但每个元素只被访问一次所以是 O(mn)搜索二维矩阵每次排除一行或一列所以是 O(mn)旋转图像转置加翻转每个元素被访问两次常数大一些但仍然是 O(n^2)。面试时能流利说出为什么是这个复杂度比背答案可信得多。最后说一个我在实际刷题中总结出的经验矩阵题最适合用“小例子驱动”的方式学习。每遇到一道新题先在纸上画一个 3 乘 3 的矩阵手动执行一遍题目要求把操作过程和中间结果记下来然后照着这个流程写代码。这个习惯让我后来哪怕拿到没见过的矩阵题也能很快找到突破口因为画图的过程本质上就是在模拟算法执行它对理解边界、发现规律帮助极大。如果你也在重走力扣 Hot 100矩阵专题绝对值得花一周左右时间认真过一遍。不用贪多把上面提到的这些题吃透再按照自己的节奏做两轮复习你会明显感觉到二维数组相关的题目不再是心理障碍。