算法题经典解析:螺旋矩阵 (Spiral Matrix) 的四大流派

 

 

一、 题目梗概

题目要求:给定一个 m x n 的二维矩阵,要求按照 顺时针螺旋顺序,返回矩阵中的所有元素。

图片

图片

💡 核心难点

这道题不涉及高深的数据结构,纯粹考察 代码边界控制能力 和 逻辑严密性。在面试中,写出能跑通的代码不难,难的是写出“优雅、无 Bug、一次 AC(通过)”的代码。

二、 算法流派大赏

将解法归纳为 四大流派。完全重复的冗余逻辑已剔除,以下为您展示最精华的实现思路及优缺点分析。

⚔️ 流派一:边界收缩派(主流教科书解法)

运行最快 空间 O(1)

思路解析:设立 top, bottom, left, right 四堵虚拟墙。每走完一条边,对应的墙就向内收缩一格。当墙壁互相交错时,遍历结束。

优缺点对比

✅ 优点:不需要额外空间,原汁原味,执行效率极高。
❌ 缺点:极易越界!许多人在 if 判定中迷失,导致死循环或重复读取。

🔥 黄金推荐代码(C++ 究极优雅版):将推墙操作 ++up 与碰撞检测完美融合,代码极度精简。

class Solution {
public:
    vector<int> spiralOrder(vector<vector<int>>& matrix) {
        if(matrix.empty() || matrix[0].empty()) return {};
        vector<int> res;
        int up = 0, down = matrix.size() - 1;
        int left = 0, right = matrix[0].size() - 1;

        while (true) { 
            // 向右走,走完后上墙往下推 (++up)
            for (int i = left; i <= right; i++) res.push_back(matrix[up][i]);
            if (++up > down) break; // 墙撞了,直接下班!

            // 向下走,走完后右墙往左推 (--right)
            for (int i = up; i <= down; i++) res.push_back(matrix[i][right]);
            if (--right < left) break;

            // 向左走,走完后下墙往上抬 (--down)
            for (int i = right; i >= left; i--) res.push_back(matrix[down][i]);
            if (--down < up) break;

            // 向上走,走完后左墙往右推 (++left)
            for (int i = down; i >= up; i--) res.push_back(matrix[i][left]);
            if (++left > right) break;
        }
        return res;
    }
};

🤖 流派二:标记探路派(扫地机器人模拟法)

逻辑最顺 适合新手

思路解析:不关注墙在哪,只控制一个“机器人”。沿着当前方向死磕,如果前方“撞墙了”或“碰到了已走过的标记(如 999, 233, INT_MAX)”,就 右转 90 度 继续走。总共走 m * n 步。

优缺点对比

✅ 优点:逻辑一条线,不需要烧脑去算四堵墙的交集,通用性极强(适用于一切走迷宫题)。
❌ 缺点:修改了原矩阵(放了路障);如果用 visited 布尔数组,则会额外消耗 O(m*n) 的内存空间。

🔥 黄金推荐代码(Python 闭眼扫地版):使用了 d = (d + 1) % 4 的方向盘黑魔法。

class Solution:
    def spiralOrder(self, matrix: List[List[int]]) -> List[int]:
        if not matrix: return []
        m, n = len(matrix), len(matrix[0])
        ans = []

        # 方向盘:右(0,1), 下(1,0), 左(0,-1), 上(-1,0)
        dx = [0, 1, 0, -1]
        dy = [1, 0, -1, 0]

        x, y, d = 0, 0, 0 # 起点坐标 (0,0),初始方向 0(右)

        for _ in range(m * n):
            ans.append(matrix[x][y])
            matrix[x][y] = 999       # 放下路障,代表已清扫

            # 探路:按当前方向看下一步
            nx, ny = x + dx[d], y + dy[d]

            # 如果越界,或者撞到了路障,猛打方向盘右转
            if not (0 <= nx < m and 0 <= ny < n) or matrix[nx][ny] == 999:
                d = (d + 1) % 4      # 核心魔法:循环转向
                nx, ny = x + dx[d], y + dy[d]

            x, y = nx, ny

        return ans

🧅 流派三:魔法剥洋葱派(Python 特供炫技)

代码最短 极度优雅

思路解析:像削洋葱一样,把矩阵的第一行削掉存起来,然后把剩下的矩阵 逆时针旋转 90 度,继续削第一行,直到矩阵为空。

优缺点对比

✅ 优点:Pythonic 审美的巅峰,代码只有短短几行。
❌ 缺点:底层疯狂创建和解包新数组,时间复杂度和空间复杂度都较高,C++ 或 Java 实现极其痛苦。

class Solution:
    def spiralOrder(self, matrix: List[List[int]]) -> List[int]:
        res = []
        while matrix:
            # 1. 切下第一行,并入答案
            res += matrix.pop(0) 

            # 2. 神仙操作:通过解包和翻转,将矩阵逆时针转 90 度
            # zip(*matrix) 按列解包,[::-1] 倒序排列
            if matrix:
                matrix = list(zip(*matrix))[::-1]

        return res

⚙️ 流派四:硬核查表与计数派(极客思维)

纯数学驱动

思路解析:这一类代码(包括提交中的 C 语言 switch-case 版和 Java 的 op 二维状态机数组版)完全摒弃了直观的图形想象。通过精密的数学偏移量和步数计数器(如记录水平方向移动次数 cx,垂直 cy)来强行计算坐标。

优缺点对比

✅ 优点:极致的逻辑压缩,展现了强大的底层数学建模能力。
❌ 缺点:“离职代码”典范。可读性为零,在真实面试中写出这种代码,不仅容易写错,还可能被面试官要求解释半个小时。

由于排版篇幅与实用性考量,此处不展示冗长的查表代码,感兴趣的同学可尝试用状态机重写此题。

三、 核心知识点随身记

无论你选择哪种流派,这道题中暴露出的几个编程语言特性都值得记入你的错题本:

  1. 前置自增/自减的妙用: if (++up > down)。电脑会 先 将 up 加 1,立刻 返回新值进行判断,极大地压缩了代码行数。
  2. 取余转向法: d = (d + 1) % 4。任何需要在上、下、左、右四个状态中循环切换的场景,直接背诵此公式。
  3. 二维矩阵的零点: 请时刻牢记,电脑的坐标系零点 (0,0) 在 左上角,向右走是列递增(y++),向下走是行递增(x++)。
  4.