最近在系统过一遍 LeetCode Hot 100刷到第 54 题螺旋矩阵的时候我停了一下。这题标着 medium代码量不大但每次写都能在边界条件上栽跟头尤其是单行、单列、以及循环退出时机这三个地方。把这道题彻底弄懂比闷头刷十道 easy 有用得多。这篇就把我这几天整理出来的东西完整写一遍题目到底在考什么、两种主流解法怎么选、四指针模拟法的每个细节为什么这么写以及实际跑代码时最容易踩的坑。Hot 100 里有很多题型代表螺旋矩阵就是模拟 边界控制的代表作。它不涉及复杂算法不依赖高级数据结构纯粹考你把一个抽象过程翻译成精确代码的能力。很多人在面试里碰到这题思路是有的代码却写不顺就是因为少了对为什么的理解。如果你也想把这题拿捏住不想再被 1xN 矩阵坑第二次这篇应该能给你一个完整的参考。1. 题目拆解螺旋矩阵到底在考什么1.1 题目描述与核心诉求先回顾一下题目本身。给定一个 m 行 n 列的矩阵 matrix要求按顺时针螺旋顺序返回矩阵中的所有元素。举个最经典的例子输入matrix [[1,2,3],[4,5,6],[7,8,9]] 输出[1,2,3,6,9,8,7,4,5]这个输出的行走路径如果画在矩阵上就是从左上角出发先往右走完第一行再往下走完最后一列再往左走完最后一行再往上走完第一列然后进入内层继续同样的循环。整个过程像是一条贪吃蛇一直贴着当前还没走过的区域的边界爬行。本质上这题考的是两件事第一能不能把螺旋这个抽象动作拆解成一组可重复的四步操作第二能不能精确控制每一轮的边界保证每个元素恰好被访问一次。前者是思路问题后者是严谨性问题。这道题为什么被划为 medium而不是 easy就是因为大多数人思路能说清楚但代码里边界条件一多就会出现重复访问或者漏访问。1.2 Hot 100 里它的位置与价值Hot 100 的设计初衷是用有限数量的题目覆盖面试最常考的能力点。螺旋矩阵能进这个榜单说明矩阵按特定顺序遍历在面试里是高频考点。我印象里字节、微软、亚马逊的面试题库里都有这类题有时候是正着螺旋输出有时候是让你生成螺旋矩阵有时候是旋转矩阵底层能力是相通的。这类题还有一个特点它考的不是你知不知道某个算法而是你能不能把逻辑想全。这正好是面试官最看重的工程素质。很多人 leetcode 刷了不少但遇到这种纯模拟题还是会卡壳就是因为平时过度依赖题解缺少自己 push 边界条件的训练。把这题吃透对后面做螺旋矩阵 II、旋转矩阵、蛇形遍历这些变形题都有直接帮助。1.3 一眼看穿题目的约束条件做题之前一定要先看约束。这道题里 m 和 n 的范围是 [0, 20] 或更大一点意味着矩阵可能为空也可能是 1 行 N 列或者 N 行 1 列。这些极端情况就是平时最容易出事的地方。我见过不少人拿到题直接写四指针写完了顺手拿 3x3 的用例一测通过了就交上去了。结果面试官问一句如果矩阵只有一行呢当场就懵了。原因就是没在动手前先想清楚一行的情况会触发哪一步一列的情况又会触发哪一步这些思考我会在后面的单行单列矩阵为什么不会重复那一节里详细展开先记住一个结论边界条件不是靠背的是靠推的。2. 方案选择两种主流解法的对比2.1 边界收缩法四指针法边界收缩法是这题最主流的解法也是我推荐的解法。它的思路非常直白用四个变量 top、bottom、left、right 表示当前还没有被遍历的矩形区域的四条边。每一轮循环按上边从左到右、右边从上到下、下边从右到左、左边从下到上的顺序把最外层一圈元素全部取走然后把四条边往中间各收缩一格。这个过程就像剥洋葱一层一层往里剥直到所有元素都被取走。四指针法的优点是空间复杂度能做到 O(1)不需要额外的 visited 数组代码也紧凑。缺点是四个边界的更新和判断逻辑密集稍微不注意就会出错但一旦理解了为什么这样写就几乎不会错。2.2 方向模拟法转向法另一种常见解法是方向模拟。用一个方向数组directions [(0, 1), (1, 0), (0, -1), (-1, 0)] // 右、下、左、上从 (0, 0) 出发按当前方向一直走走到矩阵边界或者遇到已经访问过的格子就顺时针转向。为了判断已经访问过需要额外开一个同样大小的 visited 二维数组或者直接把原数组的值改成特殊标记。方向模拟法的优点是逻辑更符合直觉尤其适合处理不规则矩阵或者起点不在左上角的情况比如 LeetCode 885 螺旋矩阵 III。缺点是空间复杂度会变成 O(mn)而且在边界判断上方向数组的下标变化也需要小心处理不然容易越界。2.3 为什么我更推荐边界收缩法单从面试角度来看边界收缩法是更优的选择。原因有三第一空间占用是 O(1)面试官问复杂度的时候可以很骄傲地说是常数空间。第二代码量更少写起来更快。第三边界收缩法逼着你去推循环条件和收缩时机这个过程本身就是面试官想看的能力。方向模拟法我也建议掌握尤其是你想扩展做其它螺旋类题型的时候它的通用性更强。但如果是应付这道题我会把主要精力放在边界收缩法上先把这块的每个细节吃透再去看方向模拟。3. 边界收缩法的核心细节拆解3.1 四个边界指针的初始化先定义四个变量它们分别代表当前未遍历区域的四条边top 0 bottom len(matrix) - 1 left 0 right len(matrix[0]) - 1注意这里的前提是矩阵非空。所以开头一定要判空如果 matrix 为空或者 matrix[0] 为空直接返回空列表。这四个变量的含义要非常清楚top 是当前最上面还没遍历到的行下标bottom 是最下面还没遍历到的行下标left 是最左边还没遍历到的列下标right 是最右边还没遍历到的列下标。每次遍历完一条边对应的指针就往中间收缩一格。这就是收缩二字的由来。3.2 一轮循环中的四步遍历在主循环里每一轮都执行四步第一步遍历上边。从 (top, left) 往右走到 (top, right)把这个范围内的元素全部加入结果。遍历完后top 加 1表示最上面这一行已经处理完了。第二步遍历右边。从 (top, left) 往右走到 (top, right)把这个范围内的元素全部加入结果。遍历完后top 加 1表示最上面这一行已经处理完了。等等这里我口误了第二步应该是从 (top, right) 往下走到 (bottom, right)也就是列下标固定在 right行下标从 top 遍历到 bottom。遍历完后right 减 1。第三步遍历下边。从 (bottom, right) 往左走到 (bottom, left)也就是行下标固定在 bottom列下标从 right 遍历到 left。遍历完后bottom 减 1。第四步遍历左边。从 (bottom, left) 往上走到 (top, left)也就是列下标固定在 left行下标从 bottom 遍历到 top。遍历完后left 加 1。画个简单的矩阵看就很清晰1 2 3 4 5 6 7 8 9第一轮上边取 1、2、3右边取 6、9下边取 8、7左边取 4。第二轮只剩中间的 5单独取出来。3.3 两个 if 判断存在的意义这里是最容易翻车的地方。第三步和第四步不是无脑直接执行而是要先做判断第三步之前判断top bottom第四步之前判断left right。为什么要加这两个判断因为经过前两步的收缩可能出现这一边已经没有元素可遍历的情况。比如在单列矩阵 [[1],[2],[3]] 中第一步遍历完上边后top 变成 1第二步遍历右边时right 变成 -1此时如果不判断第三步会尝试从 (bottom, 0) 往左遍历但右侧边界已经收缩到 -1 了会造成出错。按我的实践这俩 if 可以理解成防止对已收缩区域的重叠访问。在窄矩阵或者矮矩阵中前两步可能会把中间区域全部处理完后两步就没有存在的必要了。3.4 循环终止条件的推导主循环的终止条件是while top bottom and left right:注意是且不是或。它表示当前还存在一个至少一行且至少一列的未遍历矩形区域。一旦 top bottom说明行方向已经没有未遍历区域了一旦 left right说明列方向已经没有未遍历区域了。两者只要有一个不满足就说明矩阵已经遍历完循环应该退出。我见过有人把条件写成or结果在矩形矩阵上也能跑出正确结果但换成 3x4 这种非方阵就出问题。推理方式其实很简单区域存在的充要条件是同时存在至少一行和至少一列。用 and 才是符合几何直觉的写法。4. 完整代码实现与逐步解析4.1 Python 参考实现把上面的思路落成代码大概是这个样子def spiralOrder(matrix): if not matrix or not matrix[0]: return [] top, bottom 0, len(matrix) - 1 left, right 0, len(matrix[0]) - 1 res [] while top bottom and left right: # 上边左到右 for i in range(left, right 1): res.append(matrix[top][i]) top 1 # 右边上到下 for i in range(top, bottom 1): res.append(matrix[i][right]) right - 1 # 下边右到左先判断还有没有行 if top bottom: for i in range(right, left - 1, -1): res.append(matrix[bottom][i]) bottom - 1 # 左边下到上先判断还有没有列 if left right: for i in range(bottom, top - 1, -1): res.append(matrix[i][left]) left 1 return res这段代码我在本地跑过很多次核心逻辑没有问题。需要留意的是第三步和第四步中的 range 边界range(right, left - 1, -1)表示从 right 一直取到 left含 leftrange(bottom, top - 1, -1)表示从 bottom 一直取到 top含 top。这里的-1是步长left - 1和top - 1是为了让 range 能包含最左端和最上端。4.2 用 3x3 矩阵完整走一遍执行过程拿题目里的 3x3 矩阵来逐步执行一次初始状态top0, bottom2, left0, right2res[]。第一轮循环开始。 上边遍历range(0, 3)逐个取 (0,0)、(0,1)、(0,2)拿到 1、2、3。top 变成 1。 右边遍历range(1, 3)取 (1,2)、(2,2)拿到 6、9。right 变成 1。 下边遍历判断 top(1) bottom(2)成立。range(1, 0, -1)取 (2,1)、(2,0)拿到 8、7。bottom 变成 1。 左边遍历判断 left(0) right(1)成立。range(1, 0, -1)取 (1,0)拿到 4。left 变成 1。此时 res 是 [1,2,3,6,9,8,7,4]。进入第二轮循环判断条件top(1) bottom(1) 且 left(1) right(1)成立。 上边遍历range(1, 2)取 (1,1)拿到 5。top 变成 2。 右边遍历range(2, 1)不执行。 下边遍历判断 top(2) bottom(1)不成立跳过。注意这里没有改 bottom所以 bottom 保持 1。 左边遍历判断 left(1) right(1)成立但 range(1, 1, -1) 为空不执行任何操作。left 变成 2。循环结束判断top(2) bottom(1) 不成立退出循环。最终结果 [1,2,3,6,9,8,7,4,5]。这轮走完你应该能直观看到第二轮的下边和左边之所以需要判断就是因为这个阶段已经进入了最内层区域有些边实际上不存在了。4.3 用 3x4 矩阵验证非方阵场景再看一个 3x4 的非方阵1 2 3 4 5 6 7 8 9 10 11 12初始top0, bottom2, left0, right3。 第一轮 上边取 1、2、3、4top1。 右边取 8、12right2。 下边取 11、10、9bottom1。 左边取 5left1。 第一轮结果的前半段[1,2,3,4,8,12,11,10,9,5]。第二轮开始top1, bottom1, left1, right2条件满足。 上边取 6、7top2。 右边 range(2, 2) 为空。 下边判断 top(2) bottom(1) 不成立跳过。 左边判断 left(1) right(2) 成立但 range(1, 1, -1) 为空。 退出循环。最终结果 [1,2,3,4,8,12,11,10,9,5,6,7]。这里有一个非常值得品味的点第二轮时第 2 行其实只剩两个元素 6、7它们已经在上边遍历中被取走了所以后两步的判断全部失效这正是两个 if 存在的原因。4.4 时间与空间复杂度分析时间复杂度是 O(mn)因为每个元素只被访问一次。空间复杂度方面如果不考虑结果数组 res 占用的空间只用四个指针是 O(1)。如果面试官问结果数组算不算空间那就说 O(mn)这是题目输出的必需空间不算额外消耗。有些人会纠结 for while 的时间实际上外层循环最多循环 min(ceil(m/2), ceil(n/2)) 次内层四条边的总访问次数加起来恰好等于 m*n所以整体是线性的。这个复杂度在矩阵类题目里已经是最优了因为无论如何你都得把所有元素读一遍。5. 常见问题与调试技巧实录5.1 高频报错死循环、漏元素、多元素对照表我自己刷题和帮人看代码时发现这三类问题出现频率最高。整理成一张对照表方便排查。症状根本原因解决办法死循环边界指针没有正确收缩或循环条件用成了 or检查 top/bottom/left/right 每轮是否都在往中间移动循环条件必须用 and元素重复后两步下边、左边没有加 if 判断导致对已收缩区域重复访问下边遍历前判断 top bottom左边遍历前判断 left right元素遗漏遍历某一变时 range 边界写错少走了一格记住 range 左闭右开含右端点时要 1含左端点反向时要 -1单行矩阵报错下边或左边的循环在不该执行时执行了单行时下边遍历会被 if 挡住单列时左边遍历会被 if 挡住核心就在这两个判断5.2 单行单列矩阵为什么不会重复很多人在单行矩阵 [[1,2,3]] 上会觉得奇怪上边取完 1、2、3 后左边为什么不会把 1 再取一遍实际走一遍top0, bottom0, left0, right2。 上边取 1、2、3top1。 右边遍历 range(1, 1) 为空right1。 下边判断 top(1) bottom(0) 不成立跳过bottom 仍为 0。 左边判断 left(0) right(1) 成立但 range(bottom0, top-10, -1) 为空所以左边也不会取任何元素。关键点在于上边遍历结束后 top 已经变成 1导致左边遍历的 range 起点 bottom 小于终点 top-1所以 range 为空。这就是指针收缩产生的天然屏障。5.3 调试工具与自测用例清单我在本地调试这类矩阵题时有个习惯写一个打印当前四个指针和 res 的辅助函数。在 while 循环的每一轮末尾打印现场就能很清楚看到每个元素是在哪一步被添进去的。调试用的自测用例我建议至少准备这组[] // 空矩阵 [[]] // 空行 [[1]] // 1x1 [[1,2,3]] // 1xN [[1],[2],[3]] // Nx1 [[1,2],[3,4]] // 2x2 [[1,2,3],[4,5,6],[7,8,9]] // 3x3 [[1,2,3,4],[5,6,7,8],[9,10,11,12]] // 3x4 [[1,2,3],[4,5,6]] // 2x3这组用例能覆盖所有类型的边界情况。你可以在写完代码后一次性跑完能过就说明基本稳了。5.4 我踩过的坑下边和左边判断不能省我第一次写这题时为了代码好看把下边和左边的 if 判断省掉了结果在 3x4 的矩阵上第二轮疯狂重复。当时我花了很长时间才意识到问题不是出在 range 上而是出在没有考虑当前矩形已经退化的情况。从那以后我养成一个习惯凡是看到方向转变类题目一定要在每一次方向变换前检查当前区域是否还成立。这个习惯后来帮我在很多矩阵题上少走了弯路包括旋转矩阵和螺旋生成这类题。6. 从螺旋矩阵出发的题型扩展6.1 LeetCode 59螺旋矩阵 II这道题是螺旋矩阵的填充版。题目要求给定一个正整数 n生成一个包含 1 到 n^2 所有元素、且元素按顺时针螺旋顺序排列的 n x n 正方形矩阵。思路和读取版几乎一模一样只是把读元素变成写元素。你可以完全复用四指针法def generateMatrix(n): matrix [[0] * n for _ in range(n)] top, bottom 0, n - 1 left, right 0, n - 1 num 1 while top bottom and left right: for i in range(left, right 1): matrix[top][i] num num 1 top 1 for i in range(top, bottom 1): matrix[i][right] num num 1 right - 1 if top bottom: for i in range(right, left - 1, -1): matrix[bottom][i] num num 1 bottom - 1 if left right: for i in range(bottom, top - 1, -1): matrix[i][left] num num 1 left 1 return matrix这道题非常适合作为螺旋矩阵的课后练习因为它的边界情况和读取版完全同构你只需要确认写入位置和读取来源一一对应即可。6.2 LeetCode 48旋转矩阵旋转矩阵要求把 n x n 矩阵顺时针旋转 90 度。它和螺旋矩阵看起来不同但底层都依赖对矩阵下标的精确控制。经典的解法是先转置swap matrix[i][j] 和 matrix[j][i]再逐行翻转。也可以用四层循环做环形交换每轮把四个角的元素旋转一圈。不管哪种写法核心都是把旋转拆成可以逐格执行的操作。我建议把这两题放在一起刷。螺旋矩阵训练的是沿着边界走旋转矩阵训练的是在边界内交换都涉及对 top、bottom、left、right 这类边界的精确把握。6.3 更多变体蛇形遍历、螺旋矩阵 III 等如果把按顺序遍历矩阵当作一个大类还有很多变体蛇形/之字形遍历按 S 形逐行遍历奇数行从左到右偶数行从右到左。LeetCode 885 螺旋矩阵 III从任意起点出发向螺旋方向遍历矩阵中途会走到矩阵外需要跳过。对角线遍历按对角线方向交替往返遍历矩阵。这些题的解法各不相同但共同点是都需要明确当前方向和边界条件。所以我一直觉得螺旋矩阵不只是一道题它是一整个系列的地基。7. 刷题与面试的个人心得7.1 Hot 100 怎么刷才算刷透Hot 100 的价值在于高频和代表性但很多人刷完就忘本质上是缺少了主动整理这一步。我刷螺旋矩阵的方式是先自己想思路再看题解然后把核心题解用自己的话写一遍最后找 2 到 3 个变体题巩固。这个过程很费时间但效果是真的好。尤其是这种模拟类题目你只有在脑子里把每一步的指针变化过一遍才算真正会了。光看代码觉得哦原来是这样到面试现场大概率还是写不利索。另外我有个习惯每道题做完后整理一句话总结。螺旋矩阵的总结是四指针收缩 两个短路判断。这句话我到现在还记得遇到类似题都能快速回忆起来。7.2 面试手撕这题怎么表现面试时如果遇到螺旋矩阵我的建议是分三步走。第一步先说思路。告诉面试官我会用四个边界指针模拟螺旋遍历每轮取一圈然后往内收缩。这里主动提到两个 if 判断会让面试官觉得你考虑过边界。第二步写代码。写的过程中边写边注释可以同步说明现在是在遍历右边因为 top 已经加 1所以起点是 top。第三步测边界。写完不急着说完成主动跑一个 1xN 和 Nx1 的用例。这个动作在面试中非常加分它比代码本身更能体现工程素养。如果面试官追问能不能用方向数组做你也不用慌。先肯定两种解法都可以然后说一种空间 O(1) 的边界收缩法再说方向模拟法在非方阵场景下的通用性。这样既展示了你的知识面也展示了你的取舍能力。我在实际面试里见过不少候选人代码写得很快但边界用例一测就挂。后来我复盘发现他们大多数是背了模板没有真正理解边界收缩时两个 if 的作用。所以还是那句老话刷题不是背题是训练思路。7.3 后续还能怎么扩展最后再分享一个我最近在做的小练习把螺旋矩阵的题目改造成从任意起点、任意方向开始螺旋遍历。虽然 LeetCode 没有完全对应的题目但用一个方向数组就能实现写出来之后你对方向控制和边界处理的理解会再上一个台阶。这类自创变体的做法我建议有余力的人可以试一试。它会逼你把原来已经会的知识重新梳理一遍很多隐藏的盲区就是这样被发现的。螺旋矩阵这道题表面上是 Hot 100 里平平无奇的一道 medium但如果你愿意往深挖一步它身上能挖出来的东西比想象中多得多。
企业数字化 ERP 产品动态
相关推荐
VMware Workstation 17.6.4 下载安装与配置全指南 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/26 5:46:40
螺旋矩阵从边界收缩到方向数组:Hot100高频题解法与边界避坑指南 近半年来我刷 Hot100 练手,几乎每到数组模拟类的题目都能看到评论区在吵“螺旋矩阵到底算 easy 还是 medium”。如果你也卡在这题超过二十分钟,大概率不是不会遍历,而是“转着转着就不知道自己转到哪了”。这篇就把 54.螺旋矩阵 从题目本质、… · 2026/9/26 5:46:40
Proteus 8.17 SP2仿真环境精准搭建指南 1. 这不是普通软件安装:Proteus 8.17 SP2 仿真环境搭建的本质是什么?Proteus 8.17 SP2 不是点几下“下一步”就能用的普通工具,它是一套嵌入式系统开发的数字孪生底座。我带过二十多个高校电子类毕设团队,也给三家工业自动化企业做… · 2026/9/26 5:46:34
多层纸袋内层热封合格,外层界面容易脱层? 多层纸袋的内层热封合格性与外层界面脱层现象是包装行业中的重要课题。确保内层的热封合理,能够加强纸袋的整体强度,防止包装失效。而外层脱层的发生,常常是因为热封工艺不达标或者材料选择不当。这些问题可能影响纸袋的性能、导致包装失败。… · 2026/9/26 6:15:28
WPF MES上位机源码:产线执行系统设计与实现 1. 从标题拆需求:WPF MES 上位机在产线里到底管什么做工厂软件这行十多年,最深的体会就是:车间的软件,方案选型错了,后面怎么写都别扭。早年在 WinForms 上写上位机,界面粗糙、布局固定,车间主任… · 2026/9/26 6:15:22
基于Spring Boot的交叉路口行人非机动车流量调查统计分析系统设计 做计算机毕设这么多年,见过太多选题翻车的案例:有的做了个管理系统就交差,有的堆了一堆技术栈却讲不清业务逻辑,还有的光顾着炫技结果连基础功能都没跑通。而这个“基于Spring Boot的交叉路口行人非机动车流量调查统计分析系统”&… · 2026/9/26 6:15:22
基于SpringBoot的交叉路口行人非机动车流量统计分析系统 打开毕设选题表看到“基于SpringBoot的大数据交叉路口行人非机动车流量调查统计分析系统”这种题目,第一反应往往是:这到底算大数据还是普通管理系统?该不会要把Hadoop全家桶都装上吧?我这两年带学生做毕设,这类题被选… · 2026/9/26 6:15:22
DeepSeek+区块链:破解工业制造数据防篡改与全流程溯源难题 简介:这是一份面向工业制造、区块链及数据安全从业者的技术方案文档PDF,聚焦DeepSeek在工业制造全生命周期数据防篡改与快速溯源中的应用,适合需要落地区块链存证、数据上链与隐私保护方案的中高级工程师。文档共891页、50个大章节࿰… · 2026/9/26 6:15:22
数据库课后习题答案别硬背:当测试用例集刷,效率翻倍 简介:万常选版《数据库原理与设计》课后习题答案资源,覆盖第2至6章及第9章,适合正在学习关系模型、数据库建模、关系数据理论与模式求精的本科生、自学者作为复习与自测材料。压缩包共7个文件,含3个doc参考答案、2个sql示例脚本、… · 2026/9/26 0:00:21
OpenClaw 替代品?Hermes Agent 踩坑实录:macOS 飞书接入 TaoToken 配置 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/26 0:00:40
向下兼容与向上兼容:接口设计中的兼容性策略与工程实践 一次版本升级事故,是很多团队绕不过去的坎。线上环境里,服务端明明已经上线了新版接口,老的移动端还在照着旧文档传参数。请求一到网关,校验直接拒绝,用户操作失败,客服群炸了锅,开发群里开始互… · 2026/9/26 0:00:46