59. 螺旋矩阵 II - 力扣(LeetCode)
大致的想法是从起点开始以顺时针走到中心,有两种实现方式:一圈一圈赋值或者每走一步就赋值
方法一:按圈循环
思路:
外层循环是要循环的圈数,这里需要分奇偶讨论,若题目给出的n为偶数
由图可知,此图n等于6(6*6的格子),红色方框为遍历的圈,因此圈数为3,通过观察可知,红框数可以只看绿色线条穿过的红色边框的数量,也为3,而红色边框的数量就是每行格子数量的一半。因此循环的圈数为n/2。
若n为奇数:
和偶数类似,通过观察可以发现圈数也为n/2(下取整),但遍历完所有圈后剩余中心位置,需要最后补充。
按照以左上角为起点,设其坐标(x,y)初始为(0,0),每一圈需要知道当前的起点,显然每次遍历完起点都会向右下移动即(x+1,y+1)。然后从当前的起点开始进行赋值,要保证每一圈赋值都符合内层的规则,因此可以将循环不变量设置为从起点开始赋值,一直赋值到当前方向的倒数第二个未被赋值的格子,可以借助变量dis记录该方向走到距离边界多少个格子停止。所以每一圈可以分成四方向的运动,不妨设置为顺时针方向,每一步的坐标为(i,j)(行,列),因此第一个方向(j)从当前起点开始,一直到倒数第二个未被赋值的格子(n-dis)停止;第二个方向(i)是从上个方向的终点开始向下到(n-dis)停止;第三个方向(j)是从上一个方向开始,到(n-dis)停止,第四个方向同理。当这一圈结束后,来到的是(x+1,y)位置。下一圈刚好是在该位置的右侧,保证了每圈的赋值规则不变只需改变起点和dis即可。
最后,如果为奇数,需要给中心位置赋值。
代码:
class Solution {
public:
vector<vector<int>> generateMatrix(int n) {
vector<vector<int>> res(n,vector<int>(n, 0));
int x = 0, y = 0 ;//每圈遍历的起始坐标
int dis = 1;//每圈遍历距离边界的长度
int turn = n / 2;//需要转几圈,偶数正好,奇数需要最后对中心赋值
int count = 1 ;//为每一个位置赋值
int i, j;//每次移动时的坐标
while(turn){
i = x, j = y;
for(j; j < n - dis; j++) res[i][j] = count++;
for(i; i < n - dis; i++) res[i][j] = count++;
for(; j > y; j--) res[i][j] = count++;
for(; i > x; i--) res[i][j] = count++;
x++, y++, dis++;
turn--;
}
if(n % 2) res[n / 2][n / 2] = count;
return res;
}
};
方法二:按位置赋值
思路:
该方法详细参考59. 螺旋矩阵 II - 力扣(LeetCode)题解,主要思想为收缩可以赋值的格子,沿第一个方向起点为当前左边界,终点为当前右边界,走完就将上边界收缩(此时可赋值的范围被压缩成下面部分),之后沿第二个方向,起点为当前上边界,终点为当前下边界,走完将右边界收缩,之后沿第三个方向,起点为当前右边界,终点为当前左边界,走完将下边界收缩,之后沿第四个方向,起点为当前下边界,终点为当前上边界,走完将左边界收缩,可以发现可赋值的范围和循环进行过程是同步的。
代码:
class Solution {
public:
vector<vector<int>> generateMatrix(int n) {
vector<vector<int>> res(n,vector<int>(n, 0));
int l = 0, r = n - 1, t = 0, b = n - 1;
int sum = n * n, count = 1;
while(count <= sum){
for(int i = l; i <= r; i++) res[t][i] = count++;//在当前最上侧赋值
t++;//收束上边界
for(int i = t; i <= b; i++) res[i][r] = count++;//在当前最右侧赋值
r--;//收束右边界
for(int i = r; i >= l; i--) res[b][i] = count++;//在当前最下侧赋值
b--;//收束下边界
for(int i = b; i >= t; i--) res[i][l] = count++;//在当前最左侧赋值
l++;//收束左边界
}
return res;
}
};