#54 矩阵操作 中等

螺旋矩阵

在 LeetCode 上查看 ↗

🎧 语音讲解

开车或通勤时可听,跟着思路走一遍

速度

📋 题目描述

给你一个 mn 列的矩阵 matrix,请按照 顺时针螺旋顺序,返回矩阵中的所有元素。

示例 1

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

示例 2

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

💭 模拟答题者思考

1. 最直接:按螺旋路径手写坐标变化——从 (0,0) 出发,方向依次为右、下、左、上,遇边界或已访问格就转向;需要 visited[m][n] 防重复,时间 O(mn),额外空间 O(mn)。

2. 重复在哪里?方向数组解法每走一步都要判「是否出界 / 是否已访问」,逻辑分散在四个分支里,单行或单列时特别容易多走或漏走。

3. 关键转化:把螺旋看成一圈圈剥洋葱——每一圈固定走四条边:顶行从左到右、右列从上到下、底行从右到左(若还有多行)、左列从下到上(若还有多列),然后四条边界各向内缩 1。

4. 手推 3×3:第一圈收集 1,2,3 → 6,9 → 8,7 → 4;收缩后只剩中心 5,第二圈顶行单独收集 5,得到 [1,2,3,6,9,8,7,4,5]。

5. 边界条件:底行仅在 top < bottom 时遍历(避免与顶行重复);左列仅在 left < right 时遍历(避免与右列重复)。单行或单列矩阵靠这两条判断自然处理。

🧠 变量语义(先读这三句再编码)

变量类型语义(三句法)
anslist<int>定义:按顺时针螺旋顺序收集到的所有元素
维护:每走完一条边,就把该边上尚未访问的元素依次追加到 ans
更新:四条边(上→右→下→左)各扫一遍后,ans 长度增加当前「剩余矩形」的周长对应元素数
top, bottomint定义:当前待遍历子矩阵的上、下边界行号(含端点)
维护:每完成一圈螺旋后,top++bottom--,向内收缩一行
更新:初始 top=0, bottom=m-1;当 top > bottom 时纵向已无剩余行,停止
left, rightint定义:当前待遍历子矩阵的左、右边界列号(含端点)
维护:每完成一圈螺旋后,left++right--,向内收缩一列
更新:初始 left=0, right=n-1;当 left > right 时横向已无剩余列,停止
i, jint定义:沿当前边扫描时的行、列下标
维护:上边从左到右、右边从上到下、下边从右到左、左边从下到上,各用一层 for 推进
更新:每访问 matrix[i][j] 后立即 ans.append(...),避免重复访问

⌨️ 落码步骤

1. 若 matrix 为空直接返回 [];取 m, n,初始化 top=0, bottom=m-1, left=0, right=n-1 与空列表 ans

2. 当 top <= bottomleft <= right 时循环(当前还有未访问的子矩形)

3. 上边for j in range(left, right+1),收集 matrix[top][j]

4. 右边for i in range(top+1, bottom+1),收集 matrix[i][right]

5. 若 top < bottom下边right-1left 逆序收集 matrix[bottom][j]

6. 若 left < right左边bottom-1top+1 逆序收集 matrix[i][left]

7. 收缩边界 top++, bottom--, left++, right--,进入下一圈

8. 返回 ans

💻 代码实现

class Solution:
    def spiralOrder(self, matrix: List[List[int]]) -> List[int]:
        if not matrix or not matrix[0]:
            return []
        m, n = len(matrix), len(matrix[0])
        top, bottom = 0, m - 1
        left, right = 0, n - 1
        ans: list[int] = []

        while top <= bottom and left <= right:
            # 上边:从左到右
            for j in range(left, right + 1):
                ans.append(matrix[top][j])
            # 右边:从上到下(跳过顶角,已在上边收集)
            for i in range(top + 1, bottom + 1):
                ans.append(matrix[i][right])
            # 下边:从右到左(仅当还有多行时)
            if top < bottom:
                for j in range(right - 1, left - 1, -1):
                    ans.append(matrix[bottom][j])
            # 左边:从下到上(仅当还有多列时)
            if left < right:
                for i in range(bottom - 1, top, -1):
                    ans.append(matrix[i][left])
            top += 1
            bottom -= 1
            left += 1
            right -= 1

        return ans
class Solution {
public:
    vector<int> spiralOrder(vector<vector<int>>& matrix) {
        vector<int> ans;
        if (matrix.empty() || matrix[0].empty()) return ans;
        int m = matrix.size(), n = matrix[0].size();
        int top = 0, bottom = m - 1, left = 0, right = n - 1;

        while (top <= bottom && left <= right) {
            // 上边:从左到右
            for (int j = left; j <= right; j++)
                ans.push_back(matrix[top][j]);
            // 右边:从上到下
            for (int i = top + 1; i <= bottom; i++)
                ans.push_back(matrix[i][right]);
            // 下边:从右到左(仅当还有多行)
            if (top < bottom) {
                for (int j = right - 1; j >= left; j--)
                    ans.push_back(matrix[bottom][j]);
            }
            // 左边:从下到上(仅当还有多列)
            if (left < right) {
                for (int i = bottom - 1; i > top; i--)
                    ans.push_back(matrix[i][left]);
            }
            top++;
            bottom--;
            left++;
            right--;
        }
        return ans;
    }
};
// 时间 O(m×n),空间 O(1)(不计输出)

📈 复杂度分析

时间复杂度 O(m × n)
空间复杂度 O(1)(不计输出数组)

⚠️ 常见坑

忘记「单行/单列」判断:走完顶行和右列后,若 top == bottom 仍遍历底行,会把同一行元素重复加入;必须用 if (top < bottom)if (left < right) 保护。

方向数组 + visited 写法里,转向时机写错会导致死循环或漏元素;剥洋葱法用边界收缩,每格恰好访问一次,更不易错。

右边循环应从 top+1 开始、左边从 bottom-1top+1,否则四个角的元素会被重复收集。

🔍 必测边界 Case

Case 1:单行
matrix = [[1,2,3,4]] → [1,2,3,4](只走顶边,top==bottom 跳过底边)
Case 2:单列
matrix = [[1],[2],[3]] → [1,2,3](顶边后只走右列,left==right 跳过左边)
Case 3:1×1
matrix = [[7]] → [7](一圈只收集一个元素)
Case 4:3×3 奇数方阵
matrix = [[1,2,3],[4,5,6],[7,8,9]] → [1,2,3,6,9,8,7,4,5](中心 5 在第二圈单独收集)
Case 5:3×4 长方形
matrix = [[1,2,3,4],[5,6,7,8],[9,10,11,12]] → [1,2,3,4,8,12,11,10,9,5,6,7](非方阵同样适用边界收缩)