leetcode59.螺旋矩阵II
- 1 题目
- 2 思路
- 3 代码
- 3.1 C++版本
- 3.2 C版本
- 3.3 Java版本
- 3.4 JavaScript版本
- 4 总结:
1 题目
题源链接
给你一个正整数 n ,生成一个包含 1 到 n2 所有元素,且元素按顺时针顺序螺旋排列的 n x n 正方形矩阵 matrix 。
示例 1:
输入:n = 3
输出:[[1,2,3],[8,9,4],[7,6,5]]
示例 2:
输入:n = 1
输出:[[1]]
提示:
1 <= n <= 20
2 思路
这道题实际上也就是要处理好各种判断的边界条件,模拟整个顺时针螺旋画矩阵的过程。
- 从左到右填充上行;
- 从上到下填充右列;
- 从右到左填充下行;
- 从下到上填充左列;
对此题没太多思路的同学可以去看。Carl老师的视频讲解
这里一圈下来,我们要画每四条边,这四条边怎么画,每画一条边都要坚持一致的左闭右开,或者左开右闭的原则,这样这一圈才能按照统一的规则画下来。
那么我按照左闭右开的原则,来画一圈,大家看一下:
每一种颜色,代表一条边,我们遍历的长度,可以看出每一个拐角处的处理规则,拐角处让给新的一条边来继续画。
这也是坚持了每条边左闭右开的原则。
3 代码
3.1 C++版本
class Solution {
public:
vector<vector<int>> generateMatrix(int n) {
vector<vector<int>> res(n, vector<int>(n, 0)); // 使用vector定义一个二维数组
int startx = 0, starty = 0; // 定义每循环一个圈的起始位置
int loop = n / 2; // 每个圈循环几次,例如n为奇数3,那么loop = 1 只是循环一圈,矩阵中间的值需要单独处理
int mid = n / 2; // 矩阵中间的位置,例如:n为3, 中间的位置就是(1,1),n为5,中间位置为(2, 2)
int count = 1; // 用来给矩阵中每一个空赋值
int offset = 1; // 需要控制每一条边遍历的长度,每次循环右边界收缩一位
int i,j;
while (loop --) {
i = startx;
j = starty;
// 下面开始的四个for就是模拟转了一圈
// 模拟填充上行从左到右(左闭右开)
for (j = starty; j < n - offset; j++) {
res[startx][j] = count++;
}
// 模拟填充右列从上到下(左闭右开)
for (i = startx; i < n - offset; i++) {
res[i][j] = count++;
}
// 模拟填充下行从右到左(左闭右开)
for (; j > starty; j--) {
res[i][j] = count++;
}
// 模拟填充左列从下到上(左闭右开)
for (; i > startx; i--) {
res[i][j] = count++;
}
// 第二圈开始的时候,起始位置要各自加1, 例如:第一圈起始位置是(0, 0),第二圈起始位置是(1, 1)
startx++;
starty++;
// offset 控制每一圈里每一条边遍历的长度
offset += 1;
}
// 如果n为奇数的话,需要单独给矩阵最中间的位置赋值
if (n % 2) {
res[mid][mid] = count;
}
return res;
}
};
3.2 C版本
/**
* Return an array of arrays of size *returnSize.
* The sizes of the arrays are returned as *returnColumnSizes array.
* Note: Both returned array and *columnSizes array must be malloced, assume caller calls free().
*/
int** generateMatrix(int n, int* returnSize, int** returnColumnSizes){
//初始化返回的结果数组的大小
*returnSize = n;
*returnColumnSizes = (int*)malloc(sizeof(int) * n);
//初始化返回结果数组ans
int** ans = (int**)malloc(sizeof(int*) * n);
int i;
for(i = 0; i < n; i++) {
ans[i] = (int*)malloc(sizeof(int) * n);
(*returnColumnSizes)[i] = n;
}
//设置每次循环的起始位置
int startX = 0;
int startY = 0;
//设置二维数组的中间值,若n为奇数。需要最后在中间填入数字
int mid = n / 2;
//循环圈数
int loop = n / 2;
//偏移数
int offset = 1;
//当前要添加的元素
int count = 1;
while(loop) {
int i = startX;
int j = startY;
//模拟上侧从左到右
for(; j < startY + n - offset; j++) {
ans[startX][j] = count++;
}
//模拟右侧从上到下
for(; i < startX + n - offset; i++) {
ans[i][j] = count++;
}
//模拟下侧从右到左
for(; j > startY; j--) {
ans[i][j] = count++;
}
//模拟左侧从下到上
for(; i > startX; i--) {
ans[i][j] = count++;
}
//偏移值每次加2
offset+=2;
//遍历起始位置每次+1
startX++;
startY++;
loop--;
}
//若n为奇数需要单独给矩阵中间赋值
if(n%2)
ans[mid][mid] = count;
return ans;
}
3.3 Java版本
class Solution {
public int[][] generateMatrix(int n) {
int loop = 0; // 控制循环次数
int[][] res = new int[n][n];
int start = 0; // 每次循环的开始点(start, start)
int count = 1; // 定义填充数字
int i, j;
while (loop++ < n / 2) { // 判断边界后,loop从1开始
// 模拟上侧从左到右
for (j = start; j < n - loop; j++) {
res[start][j] = count++;
}
// 模拟右侧从上到下
for (i = start; i < n - loop; i++) {
res[i][j] = count++;
}
// 模拟下侧从右到左
for (; j >= loop; j--) {
res[i][j] = count++;
}
// 模拟左侧从下到上
for (; i >= loop; i--) {
res[i][j] = count++;
}
start++;
}
if (n % 2 == 1) {
res[start][start] = count;
}
return res;
}
}
3.4 JavaScript版本
/**
* @param {number} n
* @return {number[][]}
*/
var generateMatrix = function(n) {
let startX = startY = 0; // 起始位置
let loop = Math.floor(n/2); // 旋转圈数
let mid = Math.floor(n/2); // 中间位置
let offset = 1; // 控制每一层填充元素个数
let count = 1; // 更新填充数字
let res = new Array(n).fill(0).map(() => new Array(n).fill(0));
while (loop--) {
let row = startX, col = startY;
// 上行从左到右(左闭右开)
for (; col < startY + n - offset; col++) {
res[row][col] = count++;
}
// 右列从上到下(左闭右开)
for (; row < startX + n - offset; row++) {
res[row][col] = count++;
}
// 下行从右到左(左闭右开)
for (; col > startY; col--) {
res[row][col] = count++;
}
// 左列做下到上(左闭右开)
for (; row > startX; row--) {
res[row][col] = count++;
}
// 更新起始位置
startX++;
startY++;
// 更新offset
offset += 2;
}
// 如果n为奇数的话,需要单独给矩阵最中间的位置赋值
if (n % 2 === 1) {
res[mid][mid] = count;
}
return res;
};
4 总结:
前面思路中的东西很重要。坚持循环不变量原则。
代码里给出的处理原则都是统一的左闭右开。
而且通过代码可以看到模拟这个过程比较重要的变量有:
startx,starty :定义每循环一个圈的起始位置;
loop = n / 2 :指定每个圈循环几次;例如n为奇数3,那么loop = 1 只是循环一圈,矩阵中间的值需要单独处理
mid = n / 2 :矩阵中间的位置,例如:n为3, 中间的位置就是(1,1),n为5,中间位置为(2, 2)
count = 1 : 用来给矩阵中每一个空赋值
offset = 1 :需要控制每一条边遍历的长度,每次循环右边界收缩一位,可以在for中看到。
还有别忘记如果n为奇数时,对最中心的位置赋值。
By --Suki 2022/1/5