螺旋矩阵 II
一、题目描述
给定一个正整数 n
,请你生成一个包含 1
到 n^2
所有元素的 n x n
正方形矩阵,元素顺序按顺时针的方式进行螺旋排列。
- 示例 1:
输入:n = 3 输出:[[1,2,3],[8,9,4],[7,6,5]]
- 示例 2:
输入:n = 1 输出:[[1]]
- 提示:
1 <= n <= 20
二、思路解析
写代码就是一种魔法,你需要给自己设定一些规则,然后在规则内优雅地实现目标。
-
将矩阵视为层层剥离的洋葱
- 可以想象,我们从最外层开始填充,一直向里螺旋地给矩阵填数;当我们填完一圈外层后,再处理内层,直到全部填完。
-
使用方向数组巧妙地转弯
- 定义一个方向数组
DIRS = [(0,1), (1,0), (0,-1), (-1,0)]
,分别代表:向右、向下、向左、向上移动。 - 在遍历的时候,一旦发现下一个位置超出边界,或者已经被占用,就让方向转向下一个方向(顺时针转弯)。
- 定义一个方向数组
-
利用循环和计数
- 我们可以用一个循环从
k = 1
到n*n
,每次将k
填到矩阵的当前位置。 - 当需要拐弯时,就更新方向索引
di = (di + 1) % 4
。
- 我们可以用一个循环从
-
空间换时间
- 在某些情况下,我们也可以先构建一个空的
n x n
矩阵,然后让指针在上面“游走”。这是一个直接但有效的办法。 - 由于
n
的上限是 20,时间复杂度在 O(n^2) 级别,性能非常可观,不用担心效率问题。
- 在某些情况下,我们也可以先构建一个空的
三、详细步骤
-
初始化
- 创建一个
n x n
的矩阵ans
,初始值均为 0。 - 定义移动方向数组:
DIRS = [(0,1), (1,0), (0,-1), (-1,0)]
。 - 定义当前的坐标
(i, j)
以及当前方向索引di
,初始均为 0。
- 创建一个
-
填充数字
- 使用循环
k
从 1 遍历到n*n
:- 将当前数字
k
放到ans[i][j]
。 - 计算下一个潜在的位置
(x, y) = (i + DIRS[di][0], j + DIRS[di][1])
。 - 如果下一个位置越界,或者该位置已经被占用,说明应该转向。
di = (di + 1) % 4
,更新方向索引。
- 根据新的方向索引,更新
i
和j
为下一个实际移动坐标。
- 将当前数字
- 使用循环
-
返回结果
- 最终循环结束后,
ans
矩阵就是答案。
- 最终循环结束后,
四、代码实现
以下是使用 Python 编写的完整参考代码示例:
DIRS = ((0,1), (1,0), (0,-1), (-1,0))class Solution:def generateMatrix(self, n: int) -> List[List[int]]:i, j, di = 0, 0, 0ans = [[0] * n for _ in range(n)]for k in range(1, n*n + 1):ans[i][j] = k# 预判下一步走向x = i + DIRS[di][0]y = j + DIRS[di][1]# 如果越界或下一位置已被填充,则改变方向if x < 0 or x >= n or y < 0 or y >= n or ans[x][y] != 0:di = (di + 1) % 4# 更新下一步实际移动坐标i += DIRS[di][0]j += DIRS[di][1]return ans
五、思考与感悟
-
实现简单 but 思路不简单
这个题目乍一看很直观,只要不停地按规律拐弯即可。然而如果你不小心处理细节,在边界判定或拐弯时可能会出错。这道题表面简单,实则是对代码细节掌控力的一种考验。 -
利用方向数组是个好方法
我们只需要一次性约定好四个方向,然后在需要拐弯时通过一个简单的模运算来切换方向,这样避免了太多 if-else 逻辑,不仅可读性提升,也减少了出错几率。 -
题型延伸
- 54. 螺旋矩阵 和本题类似,只不过前者需要遍历得到一个列表,而非生成矩阵。
- 其他类型的“模拟行走”类问题,都可以借鉴此思路,比如在棋盘或网格上处理路径时,使用方向数组也是很常见的做法。
总结
解决“螺旋矩阵”类问题的核心就在于把握好方向和拐弯判断,填充即可。借助方向数组或者模拟走墙的方法,都可以使代码逻辑更直观、优雅。
感谢阅读!如果你也有其他有趣的思路,欢迎分享交流。