54. 螺旋矩阵

难度:中等 | 主题:数组、矩阵、模拟

题目

给定一个 m x n 的矩阵,请按照顺时针螺旋的顺序返回矩阵中的所有元素。

示例

 
matrix = [[1,2,3],[4,5,6],[7,8,9]]
 
输出:[1,2,3,6,9,8,7,4,5]
 

思路

先讲个故事:剥洋葱

你有一颗洋葱,从外到内一层一层剥:

  1. 从顶部从左往右刮一刀

  2. 从右侧从上往下刮一刀

  3. 从底部从右往左刮一刀

  4. 从左侧从下往上刮一刀

每剥完一层,洋葱小了一圈——四个边界各往里缩一格。直到洋葱被剥完(边界交叉)。

这就是螺旋矩阵的本质:按层遍历,每层四条边,边界收缩。


引导式推导:四边界法

用四个变量圈定当前层:

  • top:上边界行号

  • bottom:下边界行号

  • left:左边界列号

  • right:右边界列号

每一圈分四步:


graph TD

    subgraph 一圈四步

        A["① → 从左到右走顶部"]

        B["② ↓ 从上到下走右侧"]

        C["③ ← 从右到左走底部"]

        D["④ ↑ 从下到上走左侧"]

    end

    A -->|top+1| B

    B -->|right-1| C

    C -->|bottom-1| D

    D -->|left+1| A

关键问题:第三步和第四步什么时候需要跳过?

当矩阵只剩一行时(top == bottom),第一步走完顶部行后,第三步走底部行会重复同一行。

当矩阵只剩一列时(left == right),第二步走完右侧列后,第四步走左侧列会重复同一列。

所以第三步前检查 top <= bottom,第四步前检查 left <= right


逐层递进


graph LR

    subgraph 优化路径

        A["方向数组模拟<br/>O(m×n)"]

        B["四边界法<br/>O(m×n)"]

    end

    A -->|更清晰、更简洁| B

方向数组法:定义右→下→左→上四个方向,遇到边界或已访问就转向。需要 visited 矩阵或额外标记。

四边界法:直接操作边界变量,不需要 visited 矩阵,常数空间。更简洁,更推荐。


代码

 
def spiralOrder(self, 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 col in range(left, right + 1):
 
            res.append(matrix[top][col])
 
        top += 1
 
        # 第二步:从上到下走右侧
 
        for row in range(top, bottom + 1):
 
            res.append(matrix[row][right])
 
        right -= 1
 
        # 第三步:从右到左走底部(需检查行未用完)
 
        if top <= bottom:
 
            for col in range(right, left - 1, -1):
 
                res.append(matrix[bottom][col])
 
            bottom -= 1
 
        # 第四步:从下到上走左侧(需检查列未用完)
 
        if left <= right:
 
            for row in range(bottom, top - 1, -1):
 
                res.append(matrix[row][left])
 
            left += 1
 
    return res
 

复杂度

指标解释
时间O(m×n)每个元素恰好访问一次
空间O(1)仅四个边界变量(不计输出)

实战考量

频率分析

出现在:字节/美团约 25% 的矩阵题会考螺旋遍历。重点是边界条件的处理代码的整洁性

延伸思考

Q:第三步和第四步的判断条件能不能省?

A:不能。单行矩阵(如 1,2,3)或单列矩阵(如 [[1],[2],[3]])时,不加判断会导致元素重复收集。

Q:如果要求逆时针螺旋呢?

A:调整四条边的遍历顺序即可:①从上到下走左侧 ②从左到右走底部 ③从下到上走右侧 ④从右到左走顶部。

Q:如果要求螺旋填充矩阵(生成 n×n 螺旋矩阵)呢?

A:思路完全一样,把”读元素”改成”写元素”即可。是这道题最常见的变形。

Q:方向数组法什么时候更合适?

A:当矩阵不规则(比如螺旋遍历不规则多边形区域)时,方向数组法更灵活。但标准矩形矩阵,四边界法更简洁。

易错点

  • 第三步和第四步的条件判断不能忘

  • 边界更新顺序别搞混:top++ / bottom— / left++ / right—

  • while 条件 top <= bottom and left <= right 是边界未交叉


生活类比

剥洋葱 → 四边界法

你有一颗矩形洋葱,从外到内一层一层剥。

每层剥四条边——上边右刮、右边下刮、下边左刮、左边上刮。

剥完一层,四个边界往里缩一格。

直到洋葱芯被剥完。

单行或单列的洋葱——你只要剥一边或两边就够了。


相关题目

题目关系
48旋转图像矩阵遍历技巧,用翻转代替模拟
73矩阵置零原地标记技巧

→ 返回题单:LeetCode学习路线图 > 一、数组与哈希(含双指针、矩阵)

速记卡(面试闪卡)

Q1:一句话讲清「54. 螺旋矩阵」到底是什么?

A:按顺时针螺旋顺序遍历矩阵,逐层剥四条边、边界不断向内收缩。

Q2:思路 —— 怎么理解?

A:像剥洋葱:从外到内一层层剥,每圈走顶→右→底→左四条边(Layer-by-layer Traversal,逐层遍历)。

Q3:代码 —— 怎么理解?

A:用 top/bottom/left/right 四边界变量循环剥圈,单行单列要补判断(Boundary Shrinking,边界收缩)。

Q4:复杂度 —— 怎么理解?

A:时间 O(m×n) 每元素访问一次,空间 O(1) 仅四个边界变量(Time/Space Complexity,时空复杂度)。

Q5:实战考量 —— 怎么理解?

A:约 25% 矩阵题考边界处理;变形如逆时针、螺旋填充(Spiral Order,螺旋顺序)。

Q6:核心速记主线有哪些?

  • 本质:按层遍历,每层四条边,边界收缩

  • 四边界法 O(m×n) 且 O(1) 空间,无需 visited

  • 单行/单列时第三、四步要加判断防重复

  • 变形:逆时针、螺旋填充(读改写)

口诀

A:螺旋矩阵像剥葱

四边顺序顶右底左

边界收缩别越界

单行单列要防重

相关链接