54. 螺旋矩阵

54. 螺旋矩阵

!abstract 一句话本质 就是模拟"顺时针走螺旋"的过程,不是真的有数学公式,而是"沿当前方向走,撞墙(越界或已访问)就右转 90°",走满 m·n 步停。
!quote 链接

题目

给一个 m×n 矩阵,按顺时针螺旋顺序返回所有元素(从左上角出发,右→下→左→上 循环)。

思路(从暴力到最优)

最直接的想法:模拟人走螺旋的过程。

  • 维护一个"当前方向",用方向向量 [[0,1],[1,0],[0,-1],[-1,0]] 分别表示 右、下、左、上。
  • 走到下一格之前,先看"按当前方向走会不会撞墙":
    • 越界(出矩阵)
    • 已访问过
  • 撞墙就 di = (di + 1) % 4 右转,再走。
  • 走满 m·n 步就停。

怎么标记"已访问"?两个办法:

  1. 原地修改 :把访问过的格子设成一个哨兵值(如 Infinity),空间 O(1) 但破坏原矩阵。← 本解法
  2. 额外 visited 矩阵:空间 O(m·n),不破坏原矩阵。

另一种写法 ------ 收缩边界法 : 维护 top/bottom/left/right 四个指针,每走完一行/列就收缩对应边界。优点是不破坏原矩阵、空间 O(1),而且对"生成螺旋矩阵"(螺旋矩阵 II)更自然。缺点是四个方向要分别写循环,代码长一点。

收缩边界法详解

四个边界含义:

  • top:当前还没处理过的最上行
  • bottom:当前还没处理过的最下行
  • left:当前还没处理过的最左列
  • right:当前还没处理过的最右列

遍历顺序固定为:

  1. 从左到右遍历 top 行,然后 top++
  2. 从上到下遍历 right 列,然后 right--
  3. 检查 top <= bottom,再从右到左遍历 bottom 行,然后 bottom--
  4. 检查 left <= right,再从下到上遍历 left 列,然后 left++

每轮循环后,有效矩阵向内收缩一圈。步骤 3 和 4 必须先检查边界再遍历,因为单行或单列矩阵走完前两步之后,已经不存在可走的另一边了。

javascript 复制代码
var spiralOrder2 = function(matrix) {
    const m = matrix.length, n = matrix[0].length;
    let top = 0, bottom = m - 1, left = 0, right = n - 1;
    const ans = [];

    while (top <= bottom && left <= right) {
        // 向右:top 行,left → right
        for (let j = left; j <= right; j++) ans.push(matrix[top][j]);
        top++;

        // 向下:right 列,top → bottom
        for (let i = top; i <= bottom; i++) ans.push(matrix[i][right]);
        right--;

        // 向左:bottom 行,right → left
        if (top <= bottom) {
            for (let j = right; j >= left; j--) ans.push(matrix[bottom][j]);
            bottom--;
        }

        // 向上:left 列,bottom → top
        if (left <= right) {
            for (let i = bottom; i >= top; i--) ans.push(matrix[i][left]);
            left++;
        }
    }

    return ans;
};

时间复杂度同样是 O(m·n),额外空间 O(1),并且不修改原矩阵。

关键卡点

!warning 我当时卡在这

  • 方向向量顺序:必须是 右→下→左→上(顺时针),顺序错了输出全乱。
  • 越界检查必须放在"已访问检查"前面 :如果 x, y 已越界,再读 matrix[x][y] 在某些语言会直接报错(JS 里是 undefined,侥幸不崩但是坏习惯)。
  • 1×n 矩阵的隐患 :循环靠 k < m*n 卡次数终止,最后一格转弯后的越界坐标实际没被用到。能过题,但要意识到是"次数卡得准"救了你。
  • 本题是看题解后写出来的,对"收缩边界法"还不熟,下次复习时要手写一遍收缩边界法。

代码

javascript 复制代码
var spiralOrder = function(matrix) {
    // 方向向量:右、下、左、上(顺时针)
    const DIR = [[0, 1], [1, 0], [0, -1], [-1, 0]];
    const m = matrix.length, n = matrix[0].length;
    const ans = [];
    let i = 0, j = 0, di = 0;

    for (let k = 0; k < m * n; k++) {
        ans.push(matrix[i][j]);
        matrix[i][j] = Infinity;  // 标记已访问(原地修改)

        // 预判下一格
        const x = i + DIR[di][0];
        const y = j + DIR[di][1];

        // 越界 || 已访问 → 右转
        if (x < 0 || x >= m || y < 0 || y >= n || matrix[x][y] === Infinity) {
            di = (di + 1) % 4;
        }

        i += DIR[di][0];
        j += DIR[di][1];
    }
    return ans;
};

复杂度

  • 时间:O(m·n),每个格子访问一次。
  • 空间:O(1)(不算答案数组;原地修改的代价是破坏输入)。

一句话记忆点

!tip 下次看到类似题 螺旋遍历 → 方向向量 右下左上 + 撞墙右转 (di+1)%4;不能改原矩阵就上收缩边界法(top/bottom/left/right 四指针)。

相似题 / 套用模板

  • 59. 螺旋矩阵 II(生成螺旋矩阵)→ 收缩边界法更好写
  • 73. 矩阵置零(同专题,矩阵原地操作)
  • 方向向量 + 撞墙转向 模板可迁移到所有"按固定方向走、遇阻转向"的模拟题

复习记录

2026-08-11 第 1 次复习

结果:未通过 ❌(阶段 1→0,重置为 0,3 天后复习 2026-08-14)

记得牢:

  • 代码逻辑正确,用收缩边界法 AC(四边界 l/r/t/b + 每步收缩 + break 判越界),用 [[1,2,3],[4,5,6],[7,8,9]] 验证输出 [1,2,3,6,9,8,7,4,5] 正确。
  • 客观上完成了"手写收缩边界法"的要求(虽然自己没意识到)。

需要加强:

  • 方法认知混淆 :写了收缩边界法却以为是"模拟",分不清两种写法------①方向向量模拟法(指针+方向变量 di+visited 标记/原地改);②收缩边界法(四边界+收缩,无需 visited、无需方向变量)。必须能识别自己写的是哪种。
  • 不独立:自述看题解写出,复习要求凭记忆独立完成。
  • 复杂度记法 :把 O(m·n) 写成 O(N²)------量级直觉有部分道理(两维相乘),但记法不规范;O(N²) 特指单变量平方,只有方阵(m=n=N)才成立。空间方面质疑了"输出数组算不算额外空间"------约定是输出数组不计入额外空间,所以额外空间 O(1);省下的是 visited 矩阵那 O(m·n)。
  • 下次复习重点:独立默写收缩边界法 + 准确说出 O(m·n) 时间 / O(1) 额外空间 + 能区分两种模拟写法。
相关推荐
Asize18 分钟前
73. 矩阵置零
算法
微露清风1 小时前
快慢指针算法学习记录
学习·算法·快慢指针
benchmark_cc2 小时前
1000只ETF的5分钟K线如何批量获取?QuantDash分页策略与高性能Python实践
开发语言·人工智能·爬虫·python·算法·quantdash·量化数据源
sel_92 小时前
【PEFT】参数高效微调(PEFT)技术详解:从原理到 LoRA/QLoRA 实战
人工智能·python·深度学习·算法·机器学习·参数高效微调
我星期八休息2 小时前
Linux I/O多路转接—epoll
java·linux·运维·服务器·开发语言·jvm·算法
阳明山水3 小时前
Mamba与两阶段XGBoost的融合架构:面向间歇性需求的双路径预测框架
人工智能·深度学习·算法·机器学习·架构
水上冰石3 小时前
【MuJoCo从入门到精通】第2章 第一个 MuJoCo 仿真
前端·人工智能·算法
wenyq73 小时前
LeetCode 2904. Shortest and Lexicographically Smallest Beautiful String
算法·leetcode
维克兜率天3 小时前
【维克】技术指标是什么:从均线到MACD的数学原理
人工智能·笔记·python·算法·量化