59. 螺旋矩阵 II 
题目描述
给定一个正整数 n,生成一个包含 1 到 n² 所有元素,且元素按顺时针螺旋顺序排列的 n × n 矩阵。
题型判断
这是一道矩阵模拟题。数字的填写顺序固定为:
每填完最外面一圈,尚未填写的区域仍然是一个更小的正方形,因此可以维护四条边界:
top:当前未填写区域的上边界;bottom:当前未填写区域的下边界;left:当前未填写区域的左边界;right:当前未填写区域的右边界。
完成一条边后,立即将对应边界向内收缩。这样每个格子恰好只会被填写一次。
核心思路:四边界收缩
初始化时,未填写区域是整个矩阵:
每一轮按照下面四个方向填写。
1. 填写上边:从左到右
上边填写完成后,它不再属于未填写区域,所以执行 top++。
2. 填写右边:从上到下
这里从更新后的 top 开始,避免重复填写右上角。
3. 填写下边:从右到左
这里从更新后的 right 开始,避免重复填写右下角。
4. 填写左边:从下到上
这里使用更新后的 bottom 和 top,避免重复填写两个左侧角落。
为什么后两条边要检查边界
每填完一条边,未填写区域都会缩小。当矩阵收缩到只剩一行或一列时,前面的循环可能已经把最后的格子全部填完。
因此填写下边之前需要判断:
填写左边之前需要判断:
虽然本题输入一定是正方形,有些重复填写在结果上可能不容易暴露,但统一加上边界判断可以保证每个格子只访问一次,也能直接迁移到矩形螺旋遍历问题。
JavaScript 实现
示例推演
以 n = 3 为例。
初始边界:
第一轮填写最外层:
此时矩阵为:
边界收缩为:
第二轮只剩中心格,填写 9:
填写后 top > bottom,循环结束。
循环不变量
每轮 while 循环开始时始终满足:
- 边界外的格子已经按照顺时针顺序填写完成;
[top, bottom] × [left, right]是当前尚未填写的区域;number是下一个应该写入的数字。
一轮依次填写当前区域的上、右、下、左四条边,然后收缩边界。新边界包围的区域仍然全部未填写,因此不变量继续成立。
循环结束时,至少有一组边界交错,说明未填写区域为空。由于数字从 1 开始,每写入一个格子后加一,所以最终矩阵恰好包含 1 到 n²。
复杂度分析
- 时间复杂度:
O(n²)。矩阵共有n²个格子,每个格子恰好填写一次。 - 辅助空间复杂度:
O(1)。除返回矩阵外,只使用了常数个变量。 - 返回结果空间:
O(n²),用于存储生成的矩阵。
常见错误
1. 二维数组的每一行引用了同一个数组
不要这样初始化:
fill 会让所有行指向同一个数组,修改一个位置会影响多行。应为每一行分别创建数组:
2. 填完一条边后没有立即收缩边界
如果四个方向都使用最初的边界,四个角会被重复填写。正确顺序是:
3. 混淆行和列
矩阵访问格式是:
- 水平移动:行不变,修改
column; - 垂直移动:列不变,修改
row。
4. 循环边界写成 <
四条边界 top、bottom、left、right 都表示有效下标,因此遍历时通常使用 <= 或 >=。如果写成严格不等号,容易漏掉边界格子或中心格。
5. 单独处理奇数中心,但外层循环又填写了中心
按四边界写法时,while (top <= bottom && left <= right) 会自然处理奇数矩阵的中心格,不需要再写 n % 2 === 1 的特殊逻辑。
与「54. 螺旋矩阵」的关系
两道题使用相同的四边界模型:
因此掌握下面这个方向模板后,两题只差“读取”还是“写入”:
另一种结束条件
因为一定需要填写 n² 个数字,也可以使用数字作为循环条件:
这种写法同样可行,但四边界条件:
更直接地表达了“只要还有未填写区域就继续”,也更容易复用到非正方形矩阵。
一句话总结
维护 top、bottom、left、right 四条边界,按照“上、右、下、左”的顺序填写当前外圈,每完成一条边就立即向内收缩对应边界,直到未填写区域为空。

