学习来自《代码随想录》
59 螺旋矩阵II(循环不变量)
1 题目描述
给你一个正整数 n ,生成一个包含 1 到 n2 所有元素,且元素按顺时针顺序螺旋排列的 n x n 正方形矩阵 matrix 。
例如输入6
输出
[
[1, 2, 3, 4, 5, 6,],
[20,21,22,23,24, 7],
[19,32,33,34,25, 8],
[18,31,36,35,26, 9],
[17,30,29,28,27,10],
[16,15,14,13,12,11]
]
输入5
输出
[
[1, 2, 3, 4, 5],
[16,17,18,19,6],
[15,24,25,20,7],
[14,23,22,21,8],
[13,14,11,10,9]
]
2 我的思路
按1-n^顺序打印,填充二维数组,但是不知道怎么代码实现
3 题解
方法一:顺时针螺旋填充二维数组
画矩阵的过程:顺时针画每条边,画一圈需要画4边
需要提前制定画的规则(循环不变量):每条边在转弯处涉及到边界值的交叉,需要按照一个规则来画,否则遗漏或者重复边界值,规则例如左闭右开、左闭右闭、左开右闭
如何实现画矩阵的过程,需要设计哪些控制参数,
1、要画的圈数loop = n/2 ,当n=偶数时,画完圈即结束打印,当n=奇数时,画完圈还有中心值要填充。nums[n/2][n/2] = 最大数即n^2
2、打印正方形二维数组,数组元素的下标是(i,j)
3、每一圈的开始位置是nums[0][0]、nums[1][1]、nums[2][2].....nums[圈数-1][圈数-1]
4、每一边遍历范围是什么?以下以左闭右开原则为例见图
代码:
java版本:
class Solution {
public int[][] generateMatrix(int n) {
int i,j = 0; //用于二维数组的下标
int loop = n/2; //计算画的圈数,控制圈数循环
int count = 1;//用来给矩阵每个元素赋值
int start = 0;//控制每圈开始的位置
int offset = 1;//偏移量,每画一圈+1
int[][] nums = new int[n][n];//存储矩阵结果的二维数组
//每循环一次,画一圈
while(loop-- > 0){
//从左到右画上边,左闭右开
for(j = start ; j < n - offset ; j++){
nums[start][j] = count++;
}
//从上到下画右边,左闭右开
for(i = start ; i < n - offset; i++){
nums[i][j] = count++;
}
//从右往左画下边,左闭右开
for(; j > start; j--){
nums[i][j]=count++;
}
//从下到上画左边,左闭右开
for(; i > start; i--){
nums[i][j]=count++;
}
//画完一圈后,下一圈起始点横纵坐标+1, [0][0]、[1][1]、[2][2]...
start++;
//画完一圈后,每边遍历的范围n-offset要缩短
offset++;
}
//如果n是奇数,需要单独非最中心的位置赋值
if (n%2 == 1){
nums[n/2][n/2] = count;
}
return nums;
}
}
复杂度分析:
时间复杂度O(n^2) 模拟二维数组的构建
空间复杂度O(1) 除了返回的矩阵以外,空间复杂度是常数
如果算返回的矩阵,空间复杂度为O(n^2)
python版本:
1、注意二维列表的定义方法
使用列表推导式
创建一个m*n的二维列表,并初始化元素为k:
[ [k]*n for _ in range(m)]
或者
[[k for _ in range(n)] for _ in range(m)] #两层推导式
2、还有因为range()函数是创建左闭右开的列表,for循环结束后,j或i并没有取到最后开区间的值,而这个值又是下一条边横坐标或纵坐标的固定值,所以在执行完for循环后,需要额外加一条语句。
class Solution:
def generateMatrix(self, n: int) -> List[List[int]]:
count = 1
start,i,j = 0,0,0
offset = 1
loop = n//2 #旋转圈数,整除
nums = [[0] * n for _ in range(n)] #定义一个长度为n的二维数组放返回结果
while loop > 0:
for j in range(start, n - offset):
nums[start][j] = count
count += 1
j = j + 1
for i in range(start, n - offset):
nums[i][j] = count
count += 1
i = i + 1
for j in range(n - offset, start,-1):
nums[i][j] = count
count += 1
j = j - 1
for i in range(n -offset, start,-1 ):
nums[i][j] = count
count += 1
i = i - 1
start += 1
offset += 1
loop -= 1
if n%2 == 1:
nums[n//2][n//2] = count
return nums
方法二:分层填充二维数组(力扣官方解答给出的另一种思路,待学习)
总结
1、思路不难代码难
画矩阵,但是如何画矩阵,需要考虑哪些变量(每圈起始值,遍历的范围),用什么控制画矩阵的循环(圈数),并且有哪些需要特殊处理的情况(n为奇数/偶数)。还有需要遵循一个规则(循环不变量原则)画下来才不会让边界值打架。
画个实例图非常重要,更容易分析出每边的范围和变量是怎么变化的。
2、改写python过程中,二维列表不会创建,边界值有点不清晰。
关联题目
- 54.螺旋矩阵
- 剑指Offer 29.顺时针打印矩阵