螺旋矩阵 II
在 LeetCode 上查看 ↗语音讲解
开车或通勤时可听,跟着思路走一遍
题目描述
给你一个正整数 n,生成一个包含 1 到 n² 所有元素,且元素按顺时针螺旋顺序排列的 n × n 正方形矩阵 matrix。
示例 1
示例 2
模拟答题者思考
1. 最直接:模拟螺旋路径——从 (0,0) 出发,方向依次为右、下、左、上,遇边界或已填格就转向;需要 visited[n][n] 或判 0,时间 O(n²),额外空间 O(n²)。
2. 重复在哪里?方向数组写法每步都要判「是否出界 / 是否已填」,n=1 或单行单列时转向逻辑容易写错,调试成本高。
3. 关键转化:本题与 #54「螺旋矩阵」互为逆过程——#54 按边界剥洋葱读元素,本题按同样顺序写数字;复用「四边 + 边界收缩」框架,把 ans.append 换成 matrix[i][j]=num; num++ 即可。
4. 手推 n=3:第一圈写入 1,2,3 → 4 → 5 → 6 → 7,8;收缩后第二圈只剩中心,顶行写入 9,得到 [[1,2,3],[8,9,4],[7,6,5]]。
5. 边界条件:底行仅在 top < bottom 时填充(避免与顶行重复);左列仅在 left < right 时填充(避免与右列重复)。n=1 时只走顶边一圈即结束。
变量语义(先读这三句再编码)
| 变量 | 类型 | 语义(三句法) |
|---|---|---|
matrix | list<list<int>> | 定义:待填充的 n×n 结果矩阵,初始为 0维护:按顺时针螺旋顺序,从外圈到内圈逐格写入 1..n²更新:每写入一格 matrix[i][j] = num 后 num++,直至 num > n² |
num | int | 定义:下一个要写入矩阵的整数,初始为 1 维护:沿当前圈的顶行→右列→底行→左列顺序递增赋值 更新:每填一格 num += 1;当 num > n² 时全部填完,可结束 |
top, bottom | int | 定义:当前待填充子矩阵的上、下边界行号(含端点) 维护:每完成一圈螺旋后 top++、bottom--,向内收缩一行更新:初始 top=0, bottom=n-1;循环条件 num ≤ n² 自然终止 |
left, right | int | 定义:当前待填充子矩阵的左、右边界列号(含端点) 维护:每完成一圈螺旋后 left++、right--,向内收缩一列更新:初始 left=0, right=n-1;与 top/bottom 共同框定当前「洋葱圈」 |
i, j | int | 定义:沿当前边扫描时的行、列下标 维护:四条边分别用 for 推进——上从左到右、右从上到下、下从右到左、左从下到上更新:每步写入 matrix[i][j] 并递增 num,与 #54 读螺旋顺序的遍历方向完全一致,只是从「读」变为「写」 |
落码步骤
1. 创建 n×n 的零矩阵,初始化 num=1,top=0, bottom=n-1, left=0, right=n-1
2. 当 num ≤ n² 时循环(还有数字待填)
3. 上边:for j in range(left, right+1),依次 matrix[top][j]=num; num+=1
4. 右边:for i in range(top+1, bottom+1),依次 matrix[i][right]=num; num+=1
5. 若 top < bottom,下边从 right-1 到 left 逆序填充 matrix[bottom][j]
6. 若 left < right,左边从 bottom-1 到 top+1 逆序填充 matrix[i][left]
7. 收缩边界 top++, bottom--, left++, right--,进入下一圈
8. 返回 matrix
代码实现
class Solution:
def generateMatrix(self, n: int) -> List[List[int]]:
matrix = [[0] * n for _ in range(n)]
num = 1
top, bottom = 0, n - 1
left, right = 0, n - 1
while num <= n * n:
# 上边:从左到右
for j in range(left, right + 1):
matrix[top][j] = num
num += 1
# 右边:从上到下(跳过顶角,已在上边填入)
for i in range(top + 1, bottom + 1):
matrix[i][right] = num
num += 1
# 下边:从右到左(仅当还有多行时)
if top < bottom:
for j in range(right - 1, left - 1, -1):
matrix[bottom][j] = num
num += 1
# 左边:从下到上(仅当还有多列时)
if left < right:
for i in range(bottom - 1, top, -1):
matrix[i][left] = num
num += 1
top += 1
bottom -= 1
left += 1
right -= 1
return matrix
class Solution {
public:
vector<vector<int>> generateMatrix(int n) {
vector<vector<int>> matrix(n, vector<int>(n, 0));
int num = 1;
int top = 0, bottom = n - 1, left = 0, right = n - 1;
while (num <= n * n) {
// 上边:从左到右
for (int j = left; j <= right; j++) {
matrix[top][j] = num++;
}
// 右边:从上到下
for (int i = top + 1; i <= bottom; i++) {
matrix[i][right] = num++;
}
// 下边:从右到左(仅当还有多行时)
if (top < bottom) {
for (int j = right - 1; j >= left; j--) {
matrix[bottom][j] = num++;
}
}
// 左边:从下到上(仅当还有多列时)
if (left < right) {
for (int i = bottom - 1; i > top; i--) {
matrix[i][left] = num++;
}
}
top++;
bottom--;
left++;
right--;
}
return matrix;
}
};
// 时间 O(n²),空间 O(1)(不计输出)
复杂度分析
O(n²)
O(1)(不计输出矩阵)
常见坑
忘记「单行/单列」判断:走完顶行和右列后,若 top == bottom 仍填底行,会把同一格重复写入;必须用 if (top < bottom) 和 if (left < right) 保护。
与 #54 混淆方向:#54 是读已有矩阵,本题是写新矩阵;遍历顺序相同,但 #54 用 append,本题用递增 num 赋值。
右边循环应从 top+1 开始、左边从 bottom-1 到 top+1,否则四个角的数字会被重复写入或覆盖。
必测边界 Case
n = 1 → [[1]](只填顶边一格,循环一次即结束)
n = 2 → [[1,2],[4,3]](两圈:外圈 1,2,3,4 中缺中心?实际 2×2 一圈填完 4 个数)
n = 3 → [[1,2,3],[8,9,4],[7,6,5]](中心 9 在第二圈单独填入)
外圈填 1..12,内圈填 13..16(无单独中心格,全靠边界收缩)
共 400 格,O(n²) 四边循环仍高效,无需担心超时