1. 题目
给定一个正整数 n,生成一个包含 1 到 n^2 所有元素,且元素按顺时针顺序螺旋排列的正方形矩阵。
示例:
输入: 3 输出: [ [ 1, 2, 3 ], [ 8, 9, 4 ], [ 7, 6, 5 ] ]
2. 解题思路
该题目是一个模拟过程。直觉上来说,数值应该是用循环可以表示的,每次递增1,跳出循环的条件是数值到达了。
同时,数值的填充位置也是需要用循环表示的,这个循环要分解成四个边界条件。循环的判断条件是循环次数,每次递增1,直到循环次数用完,即等于0。
但其实,以上两个过程可以合并,因为可以在填充数字的过程中,计算下一个要填充的数值。不需要先创建一个数组,再将数组的数字按规律填充。
再说填充过程的分解:螺旋形,可以看作是四个数组的结合,四个左闭右开的区间的集合,每个转角都是下一个数组的开始。
3. 代码
class Solution {
public:
vector<vector<int>> generateMatrix(int n) {
// 寻找规律,矩阵可以分解成四个数组。边界缩小并不断循环。
// 数组填充方式 是在数组范围内填充递增的数字
// 停止的条件是循环结束。
vector<vector<int>> mat(n, vector<int>(n, 0));
int len = n - 1; // 表示边界
int loop = n/2; //循环次数
int x = 0; //起点
int y = 0; //起点
int count = 1; //填充的数值
int i,j;
while (loop--) {
i = x;
j = y;
for (j=y; j<y+len; j++){
mat[i][j] = count++;
}
for (i=x; i<x+len; i++){
mat[i][j] = count++;
}
for (; j>y; j--){
mat[i][j] = count++;
}
for (; i>x; i--){
mat[i][j] = count++;
}
x++;
y++;
len -= 2;
}
if (n%2) {
int mid = n/2;
mat[mid][mid] = count;
}
return mat;
}
};
tips
1. 如何表示一个区间
比如左闭右开:[a, b)。
for (int i = a; i < b; i++){
}
或者
int i = a;
while ( i < b) {
i++;
}
同时,如果区间的左右端会有规律的变化,比如长度会减少,起点会递增,正如该题所展示的。可以利用一个更大的循环语句块将这些区间包裹住,在这个循环中去改变区间的端点/长度。
一般只需要两个变量,左区间+长度,或者,左区间+右区间,就能体现出变化。这种区间的变化在“滑动窗口”中也有体现。所以要把握住选取的变量之间的关系,理清楚具体的变化规律——什么时候发生变化+如何发生变化。
2. 这道题中需要注意的一个规律是,当n为奇数时,最后的一层一定只有一个块,这时无法分成四个数组去赋值。因此需要对n进行奇偶判断,一个写法可以积累:
if (n%2) {}
//表示如果是奇数,则
3. 在倒数计数循环时,有一个写法可以积累:
while (loop--) {}
// 表示对循环进行计数,每循环一次减1,直到loop为0,此时所有要求的循环次数都完成。