hot100_螺旋矩阵

螺旋矩阵题解

hot100_螺旋矩阵

题目

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

示例 1:

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

示例 2:

输入: 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]


题解

模拟

核心思想
  1. 方向数组 dx、dy 预先定义顺时针 4 个行走顺序:右 → 下 → 左 → 上
dx = {0, 1, 0, -1}
dy = {1, 0, -1, 0}
t=0:向右;t=1:向下;t=2:向左;t=3:向上
t=(t+1)%4 实现循环切换方向
  1. 预判下一步 拿到当前方向,先算下一步坐标 nx, ny
  • 如果下一步越界,或者已经访问过 → 顺时针更换方向
  • 换完方向之后,再真正移动坐标 x,y
  1. 访问标记
  • 标准写法:开辟 vis 布尔数组标记,不破坏输入
  1. 终止条件 一共 m_n 个元素,答案数组收集满 m_n 个元素,循环结束。

本质:迷宫行走模拟,不需要维护上下左右四个边界,不用处理各种边界 break,一套逻辑兼容方阵、单行、单列、长方形矩阵。

代码
class Solution {
public:
    vector<int> spiralOrder(vector<vector<int>>& matrix) {
        int dx[] = {0, 1, 0, -1};
        int dy[] = {1, 0, -1, 0};
        int m = matrix.size();
        int n = matrix[0].size();
        vector<vector<bool>> vis(m, vector<bool>(n, false));
        vector<int> ans;
        int t = 0;
        int x = 0, y = 0;
        while(ans.size() < m * n) {
            ans.push_back(matrix[x][y]);
            vis[x][y] = true;
            int nx = x + dx[t];
            int ny = y + dy[t];
            if(nx < 0 || nx >= m || ny < 0 || ny >= n || vis[nx][ny]) {
                t = (t + 1) % 4;
            }
            x += dx[t];
            y += dy[t];
        }
        return ans;
    }
};

时间复杂度:O(mn)O(mn) 空间复杂度: 不开修改原数组,额外开一个vis[m][n]布尔数组记录访问,空间O(mn)O(mn)。

代码(空间优化)

由于-100 <= matrix[i][j] <= 100,并且最后只要返回所有矩阵元素,所以我们可以将原数组当作标记数组使用,也就是:

  • 原地赋值 200 标记已经走过(修改原矩阵)
class Solution {
public:
    vector<int> spiralOrder(vector<vector<int>>& matrix) {
        int dx[] = {0, 1, 0, -1};
        int dy[] = {1, 0, -1, 0};
        int m = matrix.size();
        int n = matrix[0].size();
        vector<int> ans;
        int t = 0;
        int x = 0, y = 0;
        while(ans.size() < n * m) {
            ans.push_back(matrix[x][y]);
            matrix[x][y] = 200;
            int nx = x + dx[t];
            int ny = y + dy[t];
            if(nx < 0 || nx >= m || ny < 0 || ny >=n || matrix[nx][ny] == 200) {
                t = (t + 1) % 4;
            }
            x += dx[t];
            y += dy[t];
        }  
        return ans;
    }
};

时间复杂度:O(mn)O(mn) 空间复杂度:O(1)O(1)

按层模拟(边界收缩)

核心思想

把矩阵看成一层一层的环形圈层,从最外层向内一层层剥离、遍历,每一层都是一个顺时针的环。

四个边界变量

  • top:当前层的上边界行
  • bottom:当前层的下边界行
  • left:当前层的左边界列
  • right:当前层的右边界列

每一层分 4 段遍历:

  1. 上边框:top行,从左到右 left → right;遍历完,这一层上边处理完毕,top++(上边界向内收缩)
  2. 右边框:right列,从上到下 top → bottom;遍历完,right--(右边界向内收缩)
  3. 下边框:bottom行,从右到左 right → left;遍历完,bottom--(下边界向内收缩)
  4. 左边框:left列,从下到上 bottom → top;遍历完,left++(左边界向内收缩)

关键终止判断

每走完一条边,立刻检查边界是否交叉:

  • 如果 top > bottom 或者 left > right,说明已经没有元素可以遍历,直接退出循环。

为什么要每一步判断? 当矩阵不是方阵(比如单行、单列、长方形),向内收缩后,可能只剩下一行 / 一列,此时不需要再走后面的边框,继续走会造成重复读取元素。

代码
class Solution {
public:
    vector<int> spiralOrder(vector<vector<int>>& matrix) {
        vector<int> res;
        if(matrix.empty()) return res;
        int top = 0;
        int bottom = matrix.size() - 1;
        int left = 0;
        int right = matrix[0].size() - 1;
        while(true) {
            for(int i = left; i <= right; i++) res.push_back(matrix[top][i]);
            top++;
            if(top > bottom) break;
            for(int i = top; i <= bottom; i++) res.push_back(matrix[i][right]);
            right--;
            if(right < left) break;
            for(int i =  right; i >= left; i--) res.push_back(matrix[bottom][i]);
            bottom--;
            if(bottom < top) break;
            for(int i = bottom; i >= top; i--) res.push_back(matrix[i][left]);
            left++;
            if(left > right) break;
        }
        return res;
    }
};

时间复杂度:O(mn)O(mn),每个元素只访问一次 空间复杂度:O(1)O(1),除输出数组,只用 4 个边界变量,不修改原矩阵。

算法对比

对比维度按层模拟(边界收缩)方向数组模拟行走
核心思想把矩阵看作多层圆环,逐层向内剥离,收缩四条边界模拟坐标一步步行走,碰壁 / 遇到已访问元素就顺时针换向
额外空间 (不计输出 ans)O(1),只用 4 个边界变量O(mn),需要 vis 标记数组;原地标记会修改输入矩阵
是否修改原输入不修改原地标记版本会修改原矩阵;vis 版本不修改,但耗内存
逻辑流程4 段遍历,每遍历一条边收缩边界,每步后判断边界交叉退出循环移动坐标,预判下一步,碰壁就切换方向,收集满m∗n个元素结束
特殊场景处理需要每段后判断边界,否则单行 / 单列会重复输出统一逻辑,天然兼容方阵、单行、单列、长方形,不用额外特殊判断
代码易错点容易忘记每一步后的 break 判断,边界交叉漏写容易搞错:先预判下一步,换向之后再移动坐标;循环条件不能写错
面试评价⭐⭐⭐⭐⭐ 最优解。O (1) 空间,面试官期望的标准解法⭐⭐⭐⭐ 思路直观,适合笔试快速写对;但空间开销大,要主动说明缺点
时间复杂度O(mn),每个元素访问 1 次O(mn),每个元素访问 1 次

54. 螺旋矩阵 - 力扣(LeetCode)

chengzi