【矩阵】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) 额外空间,这类题通常采用「边界指针 + 循环收缩」的方式,避免使用额外的访问标记数组。

算法核心思想:

  • 维护四个边界指针 :用 top、bottom、left、right 界定当前未遍历的子矩阵区域,每遍历完一条边就向内收缩一格。
  • 顺时针四步遍历:依次完成「从左到右、从上到下、从右到左、从下到上」四条边的遍历,并在每步之后检查边界是否交错,以判断是否遍历完毕。
  • 循环终止条件 :当某一步收缩后 top > bottom 或 left > 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 例题:

相关推荐
涉密IT资质笔记3 小时前
2026 涉密资质三类硬性条件对照:军工 / 集成 / 印制参数矩阵与易错点
服务器·线性代数·矩阵·创业创新
BSD_HY4 小时前
薄膜开关矩阵按键的GPIO扩展芯片选型与中断响应延迟
单片机·线性代数·矩阵
数智工坊5 小时前
视觉SLAM第11讲|回环检测:词袋模型、字典构建与相似度计算全解析
人工智能·深度学习·线性代数
数智工坊6 小时前
视觉SLAM第4讲|李群与李代数:位姿优化的数学基石
人工智能·深度学习·线性代数·cnn
数智工坊7 小时前
视觉SLAM第10讲|后端优化(下):位姿图优化、滑动窗口与图优化工程实践
人工智能·深度学习·线性代数·矩阵
数智工坊7 小时前
视觉SLAM第13讲|工程落地:双目视觉里程计系统架构设计与性能优化
人工智能·深度学习·线性代数·性能优化·矩阵·系统架构·机器人
李日华大战鸡红18 小时前
FOC状态空间方程模型推导(学习记录)
python·学习·线性代数
for_ever_love__3 天前
线性代数入门——向量、矩阵与点积,Transformer 的运算本质
线性代数·矩阵·transformer
打工仔折腾 AI3 天前
从零写一个CAD 05:以鼠标为中心的滚轮缩放矩阵顺序不能反
后端·python·线性代数·性能优化·矩阵·计算机外设·ai agent 实战
黑妹天下第一乖4 天前
第 04 讲:阿加犀 AIMO 模型优化平台与 Model Farm 模型广场实战
人工智能·嵌入式硬件·矩阵·架构·iot