【矩阵】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 例题:

相关推荐
学习论之费曼学习法14 小时前
无人值守AI内容矩阵实战(10):Windows 上让 Python 进程 7×24 活着
人工智能·windows·python·矩阵·powershell·进程守护
zhongben_keji16 小时前
家政小程序开发全解:经营模式、品类矩阵、派单约束与信用核验
线性代数·矩阵·apache
silvamux_16 小时前
微头条爆款体平台适配实测:把交付要求变成可计算的指标
java·线性代数·算法·决策树·矩阵
zyseo816 小时前
谷歌SEO 关键词研究完整方法论:从种子词到优先级矩阵
数据库·人工智能·矩阵
lvwangshu1 天前
P15569 [COCI 2025/2026 #5] 结构 / Struktura 题解
矩阵·题解·dp·推导题·矩阵加速
镜子AI2 天前
镜子AI工程实践:用 Python 自动生成 GEO 问答语料与意图覆盖矩阵(零依赖)
人工智能·python·矩阵
学习论之费曼学习法2 天前
无人值守AI内容矩阵实战(8):标题 A/B 实验的 6 个工程实现细节,隔日对比与自动判胜
人工智能·python·算法·矩阵·自动化·a/b测试·内容矩阵
晒太羊的猫3 天前
hot100矩阵问题
线性代数·矩阵
轻口味3 天前
启元无真机二开实战1:54 个接口能做什么、不能做什么——启元 Q1/T1 SDK 能力全景与版本矩阵
线性代数·矩阵·ros2·具身智能·启元机器人
Angel Q.4 天前
协方差矩阵:3DGS 里“椭球的形状说明书“
线性代数·3d·矩阵