【LeetCode Hot100 刷题日记(19/100)】54. 螺旋矩阵 —— 数组、矩阵、模拟、双指针、层序遍历🌀

🌀 题目链接:leetcode.cn/problems/spiral-matrix/

🔍 难度:中等 | 🏷️ 标签:数组、矩阵、模拟、双指针、层序遍历

⏱️ 目标时间复杂度:O(m×n)

💾 空间复杂度:O(1)(最优解) / O(m×n)(辅助空间)


🎯 题目分析

给定一个 m × n 的二维矩阵,要求按照 顺时针螺旋顺序 输出所有元素。

例如:

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

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

观察发现,路径是从左上角开始,按"右→下→左→上"的方向循环前进,遇到边界或已访问位置时转向。

这道题是典型的 "路径模拟"问题 ,核心在于如何控制移动方向与边界判断。

它在面试中常作为考察 逻辑思维能力、边界处理能力 的经典题目,尤其适合评估候选人对"状态转移"和"循环控制"的掌握程度。


✅ 核心算法及代码讲解

🧠 方法一:模拟法(使用 visited 数组)

这是最直观的解法,通过维护一个 visited 布尔矩阵来记录每个格子是否已被访问,并用方向数组控制移动方向。

🛠️ 关键点:

  • 使用四个方向向量 { {0,1}, {1,0}, {0,-1}, {-1,0} } 表示:右 → 下 → 左 → 上。
  • 当下一步越界或已访问,则顺时针旋转方向(即 (directionIndex + 1) % 4)。
  • 每次走一步,更新当前位置并标记为已访问。

💡 优点:

  • 思路清晰,容易理解。
  • 适用于任意形状的网格路径模拟。

⚠️ 缺点:

  • 需要额外 O(mn) 空间存储 visited,不够高效。
cpp 复制代码
class Solution {
private:
    static constexpr int directions[4][2] = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}}; // 右、下、左、上
public:
    vector<int> spiralOrder(vector<vector<int>>& matrix) {
        if (matrix.size() == 0 || matrix[0].size() == 0) {
            return {};
        }
        
        int rows = matrix.size(), columns = matrix[0].size();
        vector<vector<bool>> visited(rows, vector<bool>(columns)); // 访问标记
        int total = rows * columns;
        vector<int> order(total);

        int row = 0, column = 0;
        int directionIndex = 0; // 当前方向索引

        for (int i = 0; i < total; i++) {
            order[i] = matrix[row][column]; // 收集当前值
            visited[row][column] = true;    // 标记已访问

            // 计算下一个位置
            int nextRow = row + directions[directionIndex][0];
            int nextColumn = column + directions[directionIndex][1];

            // 判断是否需要转向:越界 或 已访问
            if (nextRow < 0 || nextRow >= rows || nextColumn < 0 || nextColumn >= columns || visited[nextRow][nextColumn]) {
                directionIndex = (directionIndex + 1) % 4; // 顺时针转
            }

            // 更新位置
            row += directions[directionIndex][0];
            column += directions[directionIndex][1];
        }
        return order;
    }
};

✅ 时间复杂度:O(mn)

❌ 空间复杂度:O(mn) ------ 因为用了 visited 数组


🧠 方法二:按层模拟(推荐!面试首选)✅

将整个矩阵看作若干"层",每层从外到内依次遍历。

🧩 分层思想:

  • 第一层:外圈,从 [top][left][bottom][right]
  • 第二层:内圈,缩小边界后继续
  • 直至 left > righttop > bottom 结束

🔄 遍历顺序:

  1. 上行 :从左到右 → (top, left)(top, right)
  2. 右列 :从上到下 → (top+1, right)(bottom, right)
  3. 下行 :从右到左 → (bottom, right-1)(bottom, left+1)(仅当 left < right
  4. 左列 :从下到上 → (bottom-1, left)(top+1, left)(仅当 top < bottom

✅ 优势:

  • 不需要额外空间,空间复杂度为 O(1)(不计输出数组)
  • 边界条件明确,代码简洁易读
  • 是面试官最爱的"优雅解法"
cpp 复制代码
class Solution {
public:
    vector<int> spiralOrder(vector<vector<int>>& matrix) {
        if (matrix.size() == 0 || matrix[0].size() == 0) {
            return {};
        }

        int rows = matrix.size(), columns = matrix[0].size();
        vector<int> order;
        int left = 0, right = columns - 1, top = 0, bottom = rows - 1;

        while (left <= right && top <= bottom) {
            // 1. 上行:从左到右
            for (int column = left; column <= right; column++) {
                order.push_back(matrix[top][column]);
            }
            top++; // 上边界下移

            // 2. 右列:从上到下
            for (int row = top; row <= bottom; row++) {
                order.push_back(matrix[row][right]);
            }
            right--; // 右边界左移

            // 3. 下行:从右到左(需确保有下一行且不是单行)
            if (left < right) {
                for (int column = right; column > left; column--) {
                    order.push_back(matrix[bottom][column]);
                }
            }
            bottom--; // 下边界上移

            // 4. 左列:从下到上(需确保有左列且不是单列)
            if (top < bottom) {
                for (int row = bottom; row >= top; row--) {
                    order.push_back(matrix[row][left]);
                }
            }
            left++; // 左边界右移
        }
        return order;
    }
};

✅ 时间复杂度:O(mn)

✅ 空间复杂度:O(1) ------ 仅用常数变量


🧭 解题思路(分步详解)

📌 步骤 1:初始化边界

cpp 复制代码
int left = 0, right = cols - 1, top = 0, bottom = rows - 1;

定义四个指针分别指向当前层的四条边。

📌 步骤 2:循环遍历每一层

cpp 复制代码
while (left <= right && top <= bottom)

只要还有未遍历的区域就继续。

📌 步骤 3:逐层遍历

  1. 上行top 行从 leftright
  2. 右列right 列从 top+1bottom
  3. 下行bottom 行从 right-1left+1(避免重复)
  4. 左列left 列从 bottom-1top+1(避免重复)

🚫 注意:第3、4步必须加条件判断,防止在 1xN 或 Nx1 的情况下重复添加元素!

📌 步骤 4:收缩边界

每次遍历完一层后:

cpp 复制代码
left++, right--, top++, bottom--;

直到无法再进入新层为止。


📊 算法分析

方法 时间复杂度 空间复杂度 是否推荐
模拟法(带 visited) O(mn) O(mn) ❌ 一般情况不推荐
按层模拟 O(mn) O(1) ✅ 推荐,面试首选

🔍 为什么按层更优?

  • 面试中追求 空间效率代码优雅性
  • 能体现你对"结构化思维"的理解
  • 更容易扩展到其他类似问题(如"蛇形填充"、"Z 字形打印"等)

💻 完整代码(模板格式)

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;
using ll = long long;

class Solution {
public:
    vector<int> spiralOrder(vector<vector<int>>& matrix) {
        if (matrix.size() == 0 || matrix[0].size() == 0) {
            return {};
        }

        int rows = matrix.size(), columns = matrix[0].size();
        vector<int> order;
        int left = 0, right = columns - 1, top = 0, bottom = rows - 1;

        while (left <= right && top <= bottom) {
            // 1. 上行:从左到右
            for (int column = left; column <= right; column++) {
                order.push_back(matrix[top][column]);
            }
            top++;

            // 2. 右列:从上到下
            for (int row = top; row <= bottom; row++) {
                order.push_back(matrix[row][right]);
            }
            right--;

            // 3. 下行:从右到左(仅当不是单行)
            if (left < right) {
                for (int column = right; column > left; column--) {
                    order.push_back(matrix[bottom][column]);
                }
            }
            bottom--;

            // 4. 左列:从下到上(仅当不是单列)
            if (top < bottom) {
                for (int row = bottom; row >= top; row--) {
                    order.push_back(matrix[row][left]);
                }
            }
            left++;
        }
        return order;
    }
};

// 测试
signed main(){
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);

    // 示例测试用例
    vector<vector<int>> matrix1 = {{1,2,3},{4,5,6},{7,8,9}};
    vector<vector<int>> matrix2 = {{1,2,3,4},{5,6,7,8},{9,10,11,12}};

    Solution sol;
    auto result1 = sol.spiralOrder(matrix1);
    auto result2 = sol.spiralOrder(matrix2);

    cout << "Test 1: ";
    for (auto x : result1) cout << x << " ";
    cout << endl;

    cout << "Test 2: ";
    for (auto x : result2) cout << x << " ";
    cout << endl;

    return 0;
}

✅ 输出结果:

yaml 复制代码
Test 1: 1 2 3 6 9 8 7 4 5 
Test 2: 1 2 3 4 8 12 11 10 9 5 6 7 


🌟 本期完结,下期见!🔥

👉 点赞收藏加关注,新文更新不迷路。关注专栏【算法】LeetCode Hot100刷题日记,持续为你拆解每一道热题的底层逻辑与面试技巧!

💬 欢迎留言交流你的解法或疑问!一起进步,冲向 Offer!💪


📣 下一期预告:LeetCode 热题 100 第48题 ------ 旋转图像(中等)

🔹 题目 :给你一个 n×n 的二维矩阵,将其顺时针旋转 90 度。要求 原地修改,不能使用额外的矩阵。

🔹 核心思路:利用"对角线翻转 + 水平翻转"或"分圈旋转"策略,实现 O(1) 空间原地旋转。

🔹 考点:矩阵操作、原地算法、数学变换、边界处理。

🔹 难度:中等,但非常典型,常出现在大厂面试中(如字节、腾讯、阿里)。

🔹 提示

  • 不能直接复制再赋值!
  • 尝试找规律:(i,j)(j,n-1-i),可推导出旋转公式。
  • 推荐方法:先沿主对角线转置,再水平翻转。
    💡 提示:记住这个口诀:"转置 + 翻转 = 旋转"!

📌 记住:当你在刷题时,不要只看答案,要像写这篇文章一样,深入思考每一步背后的原理、优化空间和面试价值。这才是真正提升算法能力的方式!

相关推荐
Jing_Rainbow1 小时前
【Weapp-2 /Lesson19(2025-11-04)】微信小程序“博客园”项目深度解析:从架构到细节的完整剖析📱
微信·微信小程序·程序员
美团测试工程师1 小时前
最常见的软件测试面试题及答案
软件测试·面试·职场和发展
AI大模型1 小时前
大模型开发实战篇2:调用DeepSeek的对话接口-最佳实践
程序员·agent·deepseek
AI大模型1 小时前
大模型开发实战篇1:调用DeepSeek的对话接口,即聊天机器人接口
程序员·llm·agent
地平线开发者2 小时前
征程 6 | linear 高精度输出配置方式
算法·自动驾驶
小尧嵌入式2 小时前
C++基础语法总结
开发语言·c++·stm32·单片机·嵌入式硬件·算法
white-persist2 小时前
【攻防世界】reverse | IgniteMe 详细题解 WP
c语言·汇编·数据结构·c++·python·算法·网络安全
稚辉君.MCA_P8_Java2 小时前
Gemini永久会员 归并排序(Merge Sort) 基于分治思想(Divide and Conquer)的高效排序算法
java·linux·算法·spring·排序算法
小时前端2 小时前
性能优化:从“用户想走”到“愿意留下”的1.8秒
前端·面试