【矩阵】LC 54.螺旋矩阵

文章目录

前言

本专栏文章为《LeetCode 热题 100》的刷题题解,相关内容如有侵权,立即删除。

一、题目

1、原题链接

54.螺旋矩阵

2、题目描述

二、个人思路整理

1、思路分析

核心思路:按层模拟(边界收缩法)

  1. 维护四个边界指针:
  • top = 0(上边界)
  • bottom = m - 1(下边界)
  • left = 0(左边界)
  • right = n - 1(右边界)
  1. 顺时针遍历分为四步,每走完一条边就将对应边界向内收缩 1 格,并检查是否越界:
    1. 从左向右:遍历 matrix[top][left ... right] → \rightarrow → top++,若 top > bottom 则结束
    2. 从上向下:遍历 matrix[top ... bottom][right] → \rightarrow → right--,若 left > right 则结束。
    3. 从右向左:遍历 matrix[bottom][right ... left] → \rightarrow → bottom--,若 top > bottom 则结束。
    4. 从下向上:遍历 matrix[bottom ... top][left] → \rightarrow → left++,若 left > right 则结束。

2、解题代码

cpp 复制代码
class Solution {
public:
    vector<int> spiralOrder(vector<vector<int>>& matrix) {
        // 空矩阵直接返回空数组
        // 既能覆盖 0 × 0([])的情况,也能覆盖 m × 0([[]])的边界情况
        if (matrix.empty() || matrix[0].empty()) {
            return {};
        }

        // 初始化四个边界指针,界定当前未遍历的子矩阵区域
        int top = 0, bottom = matrix.size() - 1;
        int left = 0, right = matrix[0].size() - 1;
        vector<int> ans;

        while (true) {
            // 1. 从左到右遍历当前的上边界行
            for (int j = left; j <= right; j++) {
                ans.push_back(matrix[top][j]);
            }
            // 上边界遍历完毕,向下收缩;若上边界超越下边界,说明所有元素已遍历完成
            if (++top > bottom) {
                break;
            }

            // 2. 从上到下遍历当前的右边界列
            for (int i = top; i <= bottom; i++) {
                ans.push_back(matrix[i][right]);
            }
            // 右边界遍历完毕,向左收缩;若右边界越过左边界,结束遍历
            if (--right < left) {
                break;
            }

            // 3. 从右到左遍历当前的下边界行
            for (int j = right; j >= left; j--) {
                ans.push_back(matrix[bottom][j]);
            }
            // 下边界遍历完毕,向上收缩;若下边界超越上边界,结束遍历
            if (--bottom < top) {
                break;
            }

            // 4. 从下到上遍历当前的左边界列
            for (int i = bottom; i >= top; i--) {
                ans.push_back(matrix[i][left]);
            }
            // 左边界遍历完毕,向右收缩;若左边界越过有边界,结束遍历
            if (++left > right) {
                break;
            }
        }
        
        return ans;
    }
};

复杂度分析

  • 时间复杂度: O ( m × n ) O(m \times n) O(m×n),矩阵中每个元素恰好访问一次。
  • 空间复杂度: O ( 1 ) O(1) O(1),仅需常数级别的边界变量(返回值不计入额外空间)。

三、知识风暴

边界收缩法(按层模拟) 是「螺旋矩阵」这类矩阵遍历题的核心技巧:通过维护上下左右四个边界指针,逐层向内收缩,从而按顺时针顺序访问矩阵的每一个元素。由于题目要求 O ( 1 ) O(1) O(1) 额外空间,这类题通常采用「边界指针 + 循环收缩」的方式,避免使用额外的访问标记数组。

算法核心思想

  • 维护四个边界指针 :用 topbottomleftright 界定当前未遍历的子矩阵区域,每遍历完一条边就向内收缩一格。
  • 顺时针四步遍历:依次完成「从左到右、从上到下、从右到左、从下到上」四条边的遍历,并在每步之后检查边界是否交错,以判断是否遍历完毕。
  • 循环终止条件 :当某一步收缩后 top > bottomleft > right,说明所有元素已访问完毕,立即结束循环。
  • 空间压缩 :整个过程只使用常数个边界变量,不额外开辟与矩阵规模相关的存储,空间复杂度为 O ( 1 ) O(1) O(1)。

算法变体与扩展

  1. 逆时针螺旋遍历:只需调整四条边的遍历顺序(从「从上到下」开始),即可实现逆时针方向的螺旋访问,思路完全对称。
  2. 矩形子区域遍历:当需要遍历矩阵中某个子矩形区域时,只需将四个边界指针初始化为该子区域的坐标范围,其余逻辑不变。
  3. 分层递归写法:也可以用递归的方式逐层处理,每层剥掉最外圈后递归处理内层子矩阵,代码更简洁但需注意递归深度。
  4. 边界处理:对于只有一行、一列或空矩阵的特殊情况,必须在循环开始前或每步收缩后做好越界判断,否则容易越界访问。

与其他算法的对比

  • 方向模拟法 : O ( m × n ) O(m \times n) O(m×n) 时间、 O ( 1 ) O(1) O(1) 空间,用方向数组 + 边界判断控制转向,思路直观但需要额外的方向状态管理。
  • 边界收缩法 : O ( m × n ) O(m \times n) O(m×n) 时间、 O ( 1 ) O(1) O(1) 空间,通过边界指针收缩天然避免重复访问,代码结构更清晰,是本题最优解。
  • 访问标记数组 : O ( m × n ) O(m \times n) O(m×n) 时间、 O ( m × n ) O(m \times n) O(m×n) 空间,用布尔数组记录已访问位置,思路最简单但空间开销大,不满足本题 O ( 1 ) O(1) O(1) 额外空间要求。

相关 LeetCode 例题

相关推荐
东莞市云毅网络有限公司1 天前
基于GEO的品牌信源矩阵构建:从内容生产到AI采信的全链路设计
人工智能·机器学习·矩阵
风吹心凉1 天前
错误码矩阵 · 实机兼容映射 · 契约变更规则
线性代数·矩阵
搭贝1 天前
国资报送责任制怎么建?三级责任矩阵设计
android·数据库·人工智能·线性代数·低代码·矩阵·制造
xier_ran2 天前
【infra之路】AWQ 详解:激活感知权重保护,让 W4A16 量化精度超越 GPTQ
线性代数·算法·机器学习·量化·infra
远航计算机2 天前
GEO自诊断实操手册:你的内容为什么AI搜不到?
人工智能·矩阵·aigc·媒体
晓晓_za8986682 天前
Geo 优化源码二次开发:自定义地域规则改造实操文档
java·开发语言·搜索引擎·ci/cd·矩阵
BSD_HY2 天前
薄膜开关矩阵扫描电路设计中的防鬼键措施
人工智能·算法·矩阵·人机交互·薄膜开关·源头工厂·深圳工厂
kyle~2 天前
点云配准--- 迭代最近点 ICP 求解
线性代数·算法·机器学习
kyle~2 天前
线性代数矩阵分解 --- QR分解
线性代数·矩阵