问题说明(含示例)
问题描述:给定一个正整数 n,生成一个 n x n 的正方形矩阵,矩阵中包含 1 到 n² 的所有元素,且元素按顺时针螺旋顺序排列。
示例输入 输出 解释n = 3 [[1,2,3],[8,9,4],[7,6,5]] 元素从 1 到 9 按顺时针螺旋排列:先右移填充第一行,再下移填充最后一列,再左移填充最后一行,再上移填充第一列,最后填充中心元素n = 1 [[1]] 仅 1 个元素,直接返回包含 1 的单元素矩阵n = 2 [[1,2],[4,3]] 元素 1→2→3→4 按顺时针螺旋排列(右移→下移→左移)
解题关键
核心思路是通过控制边界的螺旋遍历模拟填充过程,利用 "上、下、左、右" 四个边界变量动态收缩范围,按顺时针顺序(右→下→左→上)依次填充元素,直到所有数字(1 到 n²)都被填入矩阵。
具体步骤如下:
- 初始化 n x n 矩阵(填充 0 占位);
- 定义四个边界:
top
(上边界,初始为 0)、bottom
(下边界,初始为 n-1)、left
(左边界,初始为 0)、right
(右边界,初始为 n-1); - 从 1 开始填充数字,直到填充完 n²:
- 从左到右填充上边界行,完成后上边界上移(
top += 1
); - 从上到下填充右边界列,完成后右边界左移(
right -= 1
); - 若上边界未超过下边界,从右到左填充下边界行,完成后下边界下移(
bottom -= 1
); - 若左边界未超过右边界,从下到上填充左边界列,完成后左边界右移(
left += 1
);
- 从左到右填充上边界行,完成后上边界上移(
- 重复上述步骤,直到所有数字填充完毕。
核心逻辑 + 关键细节
一、核心逻辑:如何模拟顺时针螺旋填充?
通过 "边界收缩" 控制填充范围,将螺旋过程拆解为四次方向填充,每次填充后调整边界,确保不重复填充已有元素:
- 方向顺序固定:严格按照 "右→下→左→上" 的顺时针顺序填充,符合螺旋的自然轨迹;
- 边界动态调整:每完成一个方向的填充,对应的边界向内收缩(如右移填充完第一行后,上边界下移,避免再次填充该行);
- 边界有效性判断 :在填充 "左" 和 "上" 方向时,需判断当前边界是否仍有效(如
top <= bottom
确保下边界行未被填充过),避免越界或重复填充。
二、关键细节:避坑与优化点
- 矩阵初始化 :用
[[0]*n for _ in range(n)]
创建 n x n 矩阵,避免浅拷贝问题(如[[0]*n]*n
会导致修改一行全部同步); - 循环终止条件 :以 "当前填充数字
current <= n²
" 为循环条件,确保所有数字都被填充; - 方向填充的范围控制 :
-
左到右:列从
left
到right
(包含两端); -
上到下:行从
top
到bottom
(包含两端); -
右到左:列从
right
到left
(逆序,步长为 -1); -
range
函数参数含义range(
right, left - 1, -1) 用于生成从右边界到左边界的逆序列索引,具体参数作用:start = right
:起始列索引为当前右边界(最右侧的列);stop = left - 1
:终止条件为 "小于left - 1
时停止"(实际会包含left
列,因为当col
减到left
时,下一次会是left - 1
,此时停止);step = -1
:步长为 -1,表示列索引从大到小递减(即 "从右向左" 遍历)。
-
下到上:行从
bottom
到top
(逆序,步长为 -1);
-
- 边界收缩的时机 :每个方向填充完成后立即收缩边界(如右移填充后
top += 1
),避免下一次填充时重复使用该边界。
对应代码
python
def generateMatrix(n: int) -> list[list[int]]:
# 初始化 n x n 矩阵,用0占位
matrix = [[0] * n for _ in range(n)]
# 定义四个边界
top, bottom = 0, n - 1
left, right = 0, n - 1
current = 1 # 从1开始填充
while current <= n * n:
# 1. 左到右填充上边界行
for col in range(left, right + 1):
matrix[top][col] = current
current += 1
top += 1 # 上边界下移
# 2. 上到下填充右边界列
for row in range(top, bottom + 1):
matrix[row][right] = current
current += 1
right -= 1 # 右边界左移
# 3. 右到左填充下边界行(需确保上边界未超过下边界)
if top <= bottom:
for col in range(right, left - 1, -1):
matrix[bottom][col] = current
current += 1
bottom -= 1 # 下边界上移
# 4. 下到上填充左边界列(需确保左边界未超过右边界)
if left <= right:
for row in range(bottom, top - 1, -1):
matrix[row][left] = current
current += 1
left += 1 # 左边界右移
return matrix
对应的基础知识
实现该算法需掌握以下 Python 基础概念与操作:
-
二维列表的创建
- 语法:
[[0]*n for _ in range(n)]
生成 n 行 n 列的矩阵,每行是独立的列表; - 注意:避免使用
[[0]*n]*n
,否则修改一行会导致所有行同步变化(浅拷贝特性)。
- 语法:
-
循环与 range 的使用
- for 循环:用于按方向遍历矩阵的行或列;
- range 函数:
- 正向遍历:
range(left, right + 1)
覆盖从 left 到 right 的所有列; - 逆向遍历:
range(right, left - 1, -1)
覆盖从 right 到 left 的所有列(步长为 -1)。
- 正向遍历:
-
边界变量的更新
- 通过
top += 1
、right -= 1
等操作动态调整边界,控制填充范围; - 边界变量是 "状态标记",直接决定下一次填充的起点和终点。
- 通过
-
条件判断语句
if top <= bottom
和if left <= right
用于判断边界有效性,避免在矩阵填充后期(如只剩一行或一列时)出现重复填充。
对应的进阶知识
该问题涉及螺旋遍历的通用逻辑、复杂度分析与边界处理技巧:
-
时间复杂度:O (n²)
- 矩阵共有 n² 个元素,每个元素仅被填充一次,因此总操作次数为 n²,时间复杂度为 O (n²)。
-
空间复杂度:O (n²)
- 需创建 n x n 的矩阵存储结果,属于输出必要空间,因此空间复杂度为 O (n²)。
-
螺旋遍历的通用模型
- 核心是 "方向循环 + 边界收缩",适用于所有螺旋遍历类问题(如螺旋读取矩阵、螺旋填充矩阵);
- 方向顺序可灵活调整(如逆时针),只需修改填充的方向顺序和边界收缩逻辑。
-
奇偶 n 的差异处理
- 当 n 为奇数时,最后会剩余中心一个元素(如 n=3 时的 9),通过第四次边界判断(
left <= right
)后,会进入最后一次左到右填充,自动处理中心元素; - 当 n 为偶数时,所有元素会被四次方向填充完整覆盖,无单独中心元素。
- 当 n 为奇数时,最后会剩余中心一个元素(如 n=3 时的 9),通过第四次边界判断(
编程思维与启示
-
"拆解问题" 的思维
- 将复杂的 "螺旋填充" 拆解为 "右→下→左→上" 四个简单的直线填充步骤,通过循环重复这四个步骤,降低问题复杂度;
- 启示:面对复杂轨迹问题(如蛇形、螺旋),可拆解为有限的基础方向,通过循环实现。
-
"边界控制" 的重要性
- 用边界变量(top/bottom/left/right)精准控制填充范围,避免手动计算索引(易出错);
- 启示:涉及区间或范围的问题(如子数组、矩阵遍历),优先用边界变量标记范围,而非直接操作索引。
-
"状态同步" 的维护
- 每次填充后立即更新边界(如填充完上边界后
top += 1
),确保下一次填充的状态正确; - 启示:操作与状态变更需同步,避免因状态滞后导致逻辑错误(如重复填充同一行)。
- 每次填充后立即更新边界(如填充完上边界后
-
"条件判断" 的必要性
- 通过
if top <= bottom
等判断避免无效填充,适应不同 n 的场景(如 n=1 时无需填充下和左方向); - 启示:算法需具备 "鲁棒性",通过条件判断处理边界情况,避免一刀切逻辑。
- 通过