【螺旋矩阵全解析】一文吃透LeetCode 54/59/146,带你玩转二维数组的“花式遍历”!

449 阅读4分钟

概要
本文以初学者视角,详细讲解了LeetCode螺旋矩阵相关题目(54、59、LCR146)的解题思路与代码实现,带你掌握二维数组的螺旋遍历与生成技巧。内容通俗易懂,配有丰富注释和案例,助你轻松突破算法难关!


一、开场:你真的会“螺旋”吗?

你是否在刷LeetCode时遇到过这样的题目:让你顺时针“螺旋”地遍历一个二维数组,或者直接生成一个螺旋排列的矩阵?刚看到题目时,很多新手都会一脸懵逼:“这怎么下手?”其实,螺旋矩阵是二维数组操作的经典题型,掌握它不仅能提升你的代码能力,还能让你在面试中脱颖而出!


二、题目介绍

1. LeetCode 54:螺旋矩阵

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

示例:

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

2. LeetCode 59:螺旋矩阵 II

给定正整数 n,生成一个 n x n 的矩阵,元素从 1 到 n²,按顺时针螺旋顺序排列。

示例:

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

3. LCR 146:螺旋遍历二维数组

给定一个二维数组,返回其螺旋遍历的结果。


三、暴力法?不如用“边界收缩”!

很多同学一开始会想到用“标记法”或者“visited数组”来记录哪些元素被访问过,但这样写起来又麻烦又容易出错。其实,最优雅的做法是用边界收缩法,也就是用四个变量(上、下、左、右)来控制遍历的范围,每遍历一圈就收缩一次边界。


四、LeetCode 54 螺旋遍历代码详解

var spiralOrder = function(matrix) {
    if (!matrix.length || !matrix[0].length) return [];
    let res = [];
    let left = 0, right = matrix[0].length - 1;
    let top = 0, bottom = matrix.length - 1;
    while (left <= right && top <= bottom) {
        // 从左到右
        for (let j = left; j <= right; j++) res.push(matrix[top][j]);
        top++;
        // 从上到下
        for (let i = top; i <= bottom; i++) res.push(matrix[i][right]);
        right--;
        if (top <= bottom) {
            // 从右到左
            for (let j = right; j >= left; j--) res.push(matrix[bottom][j]);
            bottom--;
        }
        if (left <= right) {
            // 从下到上
            for (let i = bottom; i >= top; i--) res.push(matrix[i][left]);
            left++;
        }
    }
    return res;
};

技术点解析

  • 四个边界变量leftrighttopbottom,每次遍历一圈后收缩边界。
  • 顺序:右→下→左→上,循环往复。
  • 边界判断:每次遍历后都要判断是否还有剩余行/列,防止重复遍历。

五、LeetCode 59 螺旋生成代码详解

var generateMatrix = function(n) {
    let matrix = Array.from({length: n}, () => Array(n).fill(0));
    let num = 1, left = 0, right = n - 1, top = 0, bottom = n - 1;
    while (left <= right && top <= bottom) {
        for (let j = left; j <= right; j++) matrix[top][j] = num++;
        top++;
        for (let i = top; i <= bottom; i++) matrix[i][right] = num++;
        right--;
        if (top <= bottom) {
            for (let j = right; j >= left; j--) matrix[bottom][j] = num++;
            bottom--;
        }
        if (left <= right) {
            for (let i = bottom; i >= top; i--) matrix[i][left] = num++;
            left++;
        }
    }
    return matrix;
};

技术点解析

  • 初始化矩阵:用Array.from快速生成二维数组。
  • 填充顺序:和遍历类似,只不过是赋值而不是读取。
  • 计数器:用num记录当前要填入的数字。

六、LCR 146 螺旋遍历代码简析

LCR 146其实和54题本质一样,都是螺旋遍历二维数组。你可以直接复用54题的代码思路。


七、常见问题与易错点

  1. 边界条件:一定要注意每次收缩边界后,判断是否还有剩余行/列,防止死循环或重复遍历。
  2. 单行/单列矩阵:要特别小心,防止越界。
  3. 空矩阵:提前返回空数组。

八、进阶思考

  • 如果让你逆时针螺旋遍历,应该怎么改?
  • 如果矩阵不是方阵,而是m×n的长方形,代码还能用吗?
  • 如何用递归实现螺旋遍历?

这些问题都可以通过灵活调整边界变量和遍历顺序来解决,建议大家多动手实践!


九、总结

螺旋矩阵题型看似复杂,其实只要掌握了“边界收缩法”,无论是遍历还是生成,都能轻松搞定。建议大家多画图、多模拟,理解每一步的边界变化,代码自然就会写得又快又准!