【力扣100题】23. 螺旋矩阵

一、题目描述

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

示例 1:

复制代码
输入: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]

示例 2:

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

提示:

  • m == matrix.length
  • n == matrix[i].length
  • 1 <= m, n <= 10
  • -100 <= matrix[i][j] <= 100

二、解题思路总览

核心思想:按层模拟 + 边界收缩

将矩阵看成由若干层组成,从外到内逐层遍历,每层按照「右、下、左、上」的顺序遍历。

遍历顺序说明:

复制代码
第一圈:右 → 下 → 左 → 上
[[1,2,3,4],
 [5,6,7,8],  →  [1,2,3,4] → [8,12] → [11,10,9] → [5] → 回到内层
 [9,10,11,12]]

第二圈:[6,7]
方法 核心思路 时间复杂度 空间复杂度
按层模拟(本题) 每层按右、下、左、上遍历,逐层收缩边界 O(m * n) O(1)
方向数组 用方向数组控制遍历方向 O(m * n) O(1)

三、完整代码

cpp 复制代码
class Solution {
public:
    vector<int> spiralOrder(vector<vector<int>>& matrix) {
        vector<int> ans;

        int m = matrix.size();
        int n = matrix[0].size();

        int l = 0, t = 0, r = n - 1, b = m - 1;

        while (1) {
            // 1. 从左到右,遍历上层
            for (int i = l; i <= r; i++) {
                ans.push_back(matrix[t][i]);
            }
            if (++t > b) break;

            // 2. 从上到下,遍历右列
            for (int i = t; i <= b; i++) {
                ans.push_back(matrix[i][r]);
            }
            if (--r < l) break;

            // 3. 从右到左,遍历下层
            for (int i = r; i >= l; i--) {
                ans.push_back(matrix[b][i]);
            }
            if (--b < t) break;

            // 4. 从下到上,遍历左列
            for (int i = b; i >= t; i--) {
                ans.push_back(matrix[i][l]);
            }
            if (++l > r) break;
        }

        return ans;
    }
};

四、算法流程图

4.1 整体循环流程

复制代码
输入:matrix

[Step 1] 初始化
         l = 0, t = 0
         r = n - 1
         b = m - 1
         ans = empty
         |
         v
[Step 2] 进入死循环 while(1)
         |
         v
[Step 3] 遍历上层(从左到右)
         |
         v
    for i = l to r:
        ans.push_back(matrix[t][i])
         |
         v
    t++ 后判断 t > b ?
      |是
      v
    【break 退出】      ← 循环结束
      |
      |否
      v
[Step 4] 遍历右列(从上到下)
         |
         v
    for i = t to b:
        ans.push_back(matrix[i][r])
         |
         v
    r-- 后判断 r < l ?
      |是
      v
    【break 退出】      ← 循环结束
      |
      |否
      v
[Step 5] 遍历下层(从右到左)
         |
         v
    for i = r to l (递减):
        ans.push_back(matrix[b][i])
         |
         v
    b-- 后判断 b < t ?
      |是
      v
    【break 退出】      ← 循环结束
      |
      |否
      v
[Step 6] 遍历左列(从下到上)
         |
         v
    for i = b to t (递减):
        ans.push_back(matrix[i][l])
         |
         v
    l++ 后判断 l > r ?
      |是
      v
    【break 退出】      ← 循环结束
      |
      |否
      v
    回到 Step 2

4.2 具体矩阵执行流程

复制代码
输入:[[1,2,3,4],[5,6,7,8],[9,10,11,12]]

初始:l=0, t=0, r=3, b=2

第一轮循环:
  Step 1: t=0, 遍历上层 matrix[0][0..3]
          → 输出 1,2,3,4
          → t++ → t=1
          → 判断 1 > 2?不成立,继续

  Step 2: r=3, 遍历右列 matrix[1..2][3]
          → 输出 matrix[1][3]=8, matrix[2][3]=12
          → r-- → r=2
          → 判断 2 < 0?不成立,继续

  Step 3: b=2, 遍历下层 matrix[2][2..0]
          → 输出 matrix[2][2]=11, matrix[2][1]=10, matrix[2][0]=9
          → b-- → b=1
          → 判断 1 < 1?不成立,继续

  Step 4: l=0, 遍历左列 matrix[1..1][0]
          → 输出 matrix[1][0]=5
          → l++ → l=1
          → 判断 1 > 2?不成立,继续

  回到 while(1)

第二轮循环:
  Step 1: t=1, 遍历上层 matrix[1][1..2]
          → 输出 matrix[1][1]=6, matrix[1][2]=7
          → t++ → t=2
          → 判断 2 > 1?成立

  【break 退出循环】

输出:1,2,3,4,8,12,11,10,9,5,6,7

4.3 单行矩阵边界情况

复制代码
输入:[[1,2,3,4]]
m=1, n=4
l=0, t=0, r=3, b=0

第一轮循环:
  Step 1: 遍历上层 matrix[0][0..3] → 输出 1,2,3,4
          t++ → t=1
          判断 1 > 0(b=0)?成立 → break

  输出:1,2,3,4(正确)

4.4 单列矩阵边界情况

复制代码
输入:[[1],[2],[3]]
m=3, n=1
l=0, t=0, r=0, b=2

第一轮循环:
  Step 1: 遍历上层 matrix[0][0] → 输出 1
          t++ → t=1
          判断 1 > 2?不成立

  Step 2: r=0, 遍历右列 matrix[1..2][0]
          → 输出 matrix[1][0]=2, matrix[2][0]=3
          r-- → r=-1
          判断 -1 < 0?成立 → break

  输出:1,2,3(正确)

4.5 2x2 矩阵完整流程

复制代码
输入:[[1,2],[3,4]]

初始:l=0, t=0, r=1, b=1

第一轮循环:
  Step 1: 输出 1,2
  Step 2: 输出 4(t=1 > b=1 不成立,r=1 >= l=0 成立)
          r-- → r=0
          判断 0 < 0?不成立
  Step 3: 输出 matrix[1][0]=3(t=1 <= b=1 成立)
          b-- → b=0
          判断 0 < 1?成立
  Step 4: l++ → l=1,判断 1 > 0?成立 → break

输出:1,2,4,3

五、逐行解析

5.1 初始化边界变量

cpp 复制代码
int l = 0, t = 0, r = n - 1, b = m - 1;
变量 含义 初始值
l 左边界 0
t 上边界 0
r 右边界 n - 1
b 下边界 m - 1

5.2 遍历上层

cpp 复制代码
for (int i = l; i <= r; i++) {
    ans.push_back(matrix[t][i]);
}
if (++t > b) break;

说明: 从左边界到右边界,遍历上边界所在行。上边界下移后,如果上边界超过下边界,说明已经遍历完所有层,退出循环。


5.3 遍历右列

cpp 复制代码
for (int i = t; i <= b; i++) {
    ans.push_back(matrix[i][r]);
}
if (--r < l) break;

说明: 从上边界到下边界,遍历右边界所在列。右边界左移后,如果右边界小于左边界,退出循环。


5.4 遍历下层

cpp 复制代码
for (int i = r; i >= l; i--) {
    ans.push_back(matrix[b][i]);
}
if (--b < t) break;

说明: 从右边界到左边界,遍历下边界所在行。下边界上移后,如果下边界小于上边界,退出循环。


5.5 遍历左列

cpp 复制代码
for (int i = b; i >= t; i--) {
    ans.push_back(matrix[i][l]);
}
if (++l > r) break;

说明: 从下边界到上边界,遍历左边界所在列。左边界右移后,如果左边界大于右边界,退出循环。


六、复杂度分析

指标 复杂度 说明
时间复杂度 O(m * n) 每个元素恰好访问一次
空间复杂度 O(1) 只用四个边界指针和 ans 向量

七、面试追问

问题 回答要点
为什么要用 while(1) 而不是 while(l <= r && t <= b)? 代码利用四个方向的 break 来控制循环,比统一的 while 条件更简洁
break 的四个条件分别代表什么? t > b:上层已遍历完;r < l:右列已遍历完;b < t:下层已遍历完;l > r:左列已遍历完
为什么单行矩阵不会重复遍历? t++ 后判断 t > b 直接 break,第一轮循环的 Step 2-4 因 r < l 等条件被跳过
四个 for 循环的遍历方向是什么? 上层(从左到右)、右列(从上到下)、下层(从右到左)、左列(从下到上)
什么时候循环一定会退出? 每次循环至少遍历一行或一列,边界向内收缩,最多 m+n 步必然退出
如果矩阵是 1x1 呢? 只执行 Step 1,输出唯一元素,然后 t++ > b 触发 break
进阶:能否用方向数组实现? 可以,用 dir[4] = {0,1,2,3} 表示四个方向,每次转向用 dirIdx = (dirIdx+1)%4

八、相关题目

题号 题目 关键点
54 螺旋矩阵 本题
59 螺旋矩阵 II 逆过程,根据螺旋顺序生成矩阵
885 螺旋矩阵 III 起始位置和方向不同
48 旋转图像 矩阵旋转 90 度

相关推荐
影sir1 小时前
不同测试数据下,该如何选择算法
算法·深度优先
潇湘散客2 小时前
CAX软件插件化设计实现牛刀小试
c++·算法·图形学·opengl
速易达网络2 小时前
2026,视觉算法正在经历一场静默革命
算法
WBluuue2 小时前
Codeforces 1094 Div1+2(ABCDE)
c++·算法
TENSORTEC腾视科技2 小时前
腾视科技大模型一体机解决方案:低成本私有化落地,重塑行业智能应用新格局
大数据·人工智能·科技·算法·ai·零售·大模型一体机
夏日听雨眠3 小时前
数据结构(循环队列)
数据结构·算法·链表
平行侠3 小时前
30MacLaren-Marsaglia算法故事文件
数据结构·算法
灵动小溪3 小时前
claude code工具PC安装部署
人工智能·算法
Asa121384 小时前
Nature Microbiology|跨微生物界菌株水平传播推断的新算法TRACS
算法