题目描述

✅ 59. 螺旋矩阵 II

:::fold 历史考题

考察公司:小米

:::

image-20260928202053881

image-20260928202053882

题意分析

创建一个 n × n 的正方形矩阵,把整数 1 到 n² 按顺时针螺旋顺序填入:从左上角开始向右,沿外圈依次转向下、左、上,再进入内圈继续。

每个数使用一次,每个格子也只填写一次。目标是生成矩阵,不是从已有矩阵读取元素;奇数边长最后会剩一个中心格,也需要按顺序写入。

解法:四边界螺旋填充

核心思路

[!blue]

不必为每个格子单独记录是否访问过。用 top、bottom、left、right 四个边界描述尚未填写的矩形,边界外都已完成;用 value 保存下一个要写入的数。

每轮依次处理当前矩形的上、右、下、左四条边。写完上边后立刻 top++,右边就从新的上边界开始,不会再次覆盖右上角。写完右边后立刻 right--,下边也使用新的右端点;下边、左边同理。

因为每条边写完便被移出未处理区域,相邻两条边即使原本共享拐角,后处理的一边也不再包含它。这样保证不重复;每轮剩下的又是更小的矩形,所以继续套用相同流程,就不会遗漏内圈。

当剩余区域只是一行、一列或一个中心格时,一轮未必还存在四条不同的边。代码在下边和左边前检查边界,并让所有循环使用已经收缩的范围;没有剩余格子时循环区间为空,不会再次写入。上下或左右边界交叉后,整个矩阵已经填完。

解题步骤

  1. 创建 n × n 的矩阵,四条边界初始化为最外圈,令 value = 1。
  2. 在上下、左右边界均未交叉时,从左到右填写上边,每写一格递增 value,随后将上边界下移。
  3. 从新的上边界向下填写右边,随后将右边界左移。
  4. 若上下边界仍有效,从右到左填写下边,再将下边界上移。
  5. 若左右边界仍有效,从下到上填写左边,再将左边界右移。
  6. 继续处理内圈,边界交叉后返回矩阵。

代码实现

class Solution {
    public int[][] generateMatrix(int n) {
        int[][] matrix = new int[n][n];
        int top = 0;
        int bottom = n - 1;
        int left = 0;
        int right = n - 1;
        int value = 1;

        while (top <= bottom && left <= right) {
            for (int col = left; col <= right; col++) {
                matrix[top][col] = value++;
            }

            // 一条边填完立即收缩,下一条边使用更新后的端点。
            top++;

            for (int row = top; row <= bottom; row++) {
                matrix[row][right] = value++;
            }

            right--;

            if (top <= bottom) {
                for (int col = right; col >= left; col--) {
                    matrix[bottom][col] = value++;
                }

                bottom--;
            }

            if (left <= right) {
                // 左边界仍存在时,再从下到上填充左边。
                for (int row = bottom; row >= top; row--) {
                    matrix[row][left] = value++;
                }

                left++;
            }
        }

        return matrix;
    }
}
func generateMatrix(n int) [][]int {
    matrix := make([][]int, n)
    for i := 0; i < n; i++ {
        matrix[i] = make([]int, n)
    }

    top := 0
    bottom := n - 1
    left := 0
    right := n - 1
    value := 1

    for top <= bottom && left <= right {
        for col := left; col <= right; col++ {
            matrix[top][col] = value
            value++
        }
        // 一条边填完立即收缩,下一条边使用更新后的端点。
        top++
        for row := top; row <= bottom; row++ {
            matrix[row][right] = value
            value++
        }
        right--
        if top <= bottom {
            for col := right; col >= left; col-- {
                matrix[bottom][col] = value
                value++
            }
            bottom--
        }
        if left <= right {
            // 显式检查剩余列范围,再按收缩后的边界填充左侧。
            for row := bottom; row >= top; row-- {
                matrix[row][left] = value
                value++
            }
            left++
        }
    }

    return matrix
}

复杂度分析

  • 时间复杂度:$O(n^2)$。每个格子恰好填写一次,且输出本身就有 n² 个元素。
  • 空间复杂度:$O(1)$。除返回矩阵外只使用四条边界和一个计数器。

关键点总结

[!green]

  • 四条边界表示剩余未填区域,不是最初矩阵边界,后续循环必须使用更新后的值。
  • 每填完一边立即收缩,对应的拐角不会再出现在下一条边中。
  • 剩余区域不断缩小直到为空,从而保证每个格子都被且只被填入一次。

易错点总结

[!yellow]

  • 填下一条边时仍使用旧起点,会重新写入刚处理过的拐角,并导致后面的数全部错位。
  • 写完边后忘记收缩,会重复处理同一区域,甚至无法结束。
  • 向左或向上时仍递增下标,会破坏顺时针顺序或越界。
  • 无视剩余区域是否为空,会在单行、单列或中心格处重复写入;边界判断和循环范围要配套。
  • Go 只分配外层切片不足以写入二维矩阵,还需要为每一行分配长度为 n 的切片。

相似题目

题目 难度 关联与区别
54. 螺旋矩阵 中等 沿螺旋顺序移动的边界条件相同,本题写入递增数字,原题读取既有元素。
885. 螺旋矩阵 III 中等 同样维护旋转方向,原题按逐步增长的步长从任意起点向外走。
转载与许可
作者
链接 https://hgnulb.github.io/blog/2026/80898555
许可 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处!