当前版本号[20230810]。
版本 | 修改说明 |
---|---|
20230810 | 初版 |
力扣题目链接
更多内容可点击此处跳转到代码随想录,看原版文件
给定一个正整数 n,生成一个包含 1 到 n^2 所有元素,且元素按顺时针顺序螺旋排列的正方形矩阵。
示例:
输入: 3 输出: [ [ 1, 2, 3 ], [ 8, 9, 4 ], [ 7, 6, 5 ] ]
这道题目可以说在面试中出现频率较高的题目,本题并不涉及到什么算法,就是模拟过程,但却十分考察对代码的掌控能力。
要如何画出这个螺旋排列的正方形矩阵呢?
相信很多同学刚开始做这种题目的时候,上来就是一波判断猛如虎。
结果运行的时候各种问题,然后开始各种修修补补,最后发现改了这里那里有问题,改了那里这里又跑不起来了。
大家还记得我们讲解的二分法,提到如果要写出正确的二分法一定要坚持循环不变量原则。
而求解本题依然是要坚持循环不变量原则。
模拟顺时针画矩阵的过程:
由外向内一圈一圈这么画下去。就像下图一样:
可以发现这里的边界条件非常多,在一个循环中,如此多的边界条件,如果不按照固定规则来遍历,那就是一进循环深似海,从此offer是路人。
这里一圈下来,我们要画每四条边,这四条边怎么画,每画一条边都要坚持一致的左闭右开,或者左开右闭的原则,这样这一圈才能按照统一的规则画下来。
那么我按照左闭右开的原则,来画一圈,大家看一下:
这里每一种颜色,代表一条边,我们遍历的长度,可以看出每一个拐角处的处理规则,拐角处让给新的一条边来继续画。
这也是坚持了每条边左闭右开的原则。
一些同学做这道题目之所以一直写不好,代码越写越乱。
就是因为在画每一条边的时候,一会左开右闭,一会左闭右闭,一会又来左闭右开,岂能不乱。
代码如下,已经详细注释了每一步的目的,可以看出while循环里判断的情况是很多的,代码里处理的原则也是统一的左闭右开。
通过模拟四个方向的填充过程,将数字从1开始按顺时针螺旋的方式填入矩阵中。
整体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;
}
}
这段代码使用左闭右闭的方式来定义边界。我们使用四个变量 left
、right
、top
、bottom
来表示矩阵的边界,初始时 left=0
,right=n-1
,top=0
,bottom=n-1
。
top
,列号从 left
到 right
(包括 left
和 right
),逐个填充数字 count
,并将 count
加 1。然后,我们将 top
的值加 1。right
,行号从 top
到 bottom
(包括 top
和 bottom
),逐个填充数字 count
,并将 count
加 1。然后,我们将 right
的值减 1。bottom
,列号从 right
到 left
(包括 right
和 left
),逐个填充数字 count
,并将 count
加 1。然后,我们将 bottom
的值减 1。left
,行号从 bottom
到 top
(包括 bottom
和 top
),逐个填充数字 count
,并将 count
加 1。然后,我们将 left
的值加 1。重复上述步骤,直到 count
的值达到 n*n。循环结束后,我们生成了按顺时针顺序螺旋排列的正方形矩阵。
最后,我们返回生成的结果矩阵 res
。
class Solution {
public int[][] generateMatrix(int n) {
int[][] res = new int[n][n];
int count = 1; // 定义填充数字
int left = 0, right = n - 1, top = 0, bottom = n - 1; // 定义边界
while (count <= n * n) {
// 从左到右填充上侧的行
for (int i = left; i <= right; i++) {
res[top][i] = count++;
}
top++;
// 从上到下填充右侧的列
for (int i = top; i <= bottom; i++) {
res[i][right] = count++;
}
right--;
// 从右到左填充下侧的行
for (int i = right; i >= left; i--) {
res[bottom][i] = count++;
}
bottom--;
// 从下到上填充左侧的列
for (int i = bottom; i >= top; i--) {
res[i][left] = count++;
}
left++;
}
return res;
}
}
左闭右开方法和左闭右闭方法的区别在于边界的定义方式。
首先,定义变量 loop
表示当前的螺旋圈数,初始值为 0。螺旋圈数表示当前正在填充的圈数,总共会填充 n/2 圈。
然后,创建一个大小为 n×n 的空矩阵 res
,用来存储结果。
开始循环,每次循环填充一圈。循环的条件是 loop++ < n / 2
,保证循环次数不超过 n/2。
j
作为列的索引,从 start
开始,一直到 n - loop - 1
(因为最后一列由右侧从上到下填充),逐个填充数字 count
,并将 count
加 1。i
作为行的索引,从 start
开始,一直到 n - loop - 1
(因为最后一行由下侧从右到左填充),逐个填充数字 count
,并将 count
加 1。i
,而列号从 j
开始递减,直到 loop
。同样,逐个填充数字 count
,并将 count
加 1。j
,而行号从 i
开始递减,直到 loop + 1
。逐个填充数字 count
,并将 count
加 1。每填充完一圈,我们将变量 start
加 1,表示下一圈的起始位置。重复上述步骤,直到循环结束。
如果 n 是奇数,最后还需要填充矩阵的中心点,即 res[start][start]
,此时的 count 不需要加 1。
最后,返回生成的结果矩阵 res
。
整个过程按照数学上定义的顺时针顺序,从外圈向内圈逐步填充数字,最终生成了按顺时针顺序螺旋排列的正方形矩阵。具体也可以通过下图进行一个理解。
测试代码如下:
package shuzhu;
public class Day05 {
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) { // 循环次数不超过 n/2
// 模拟上侧从左到右
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;
}
public static void main(String[] args) {
Day05 solution = new Day05();
// 测试生成 3x3 的螺旋矩阵
int[][] matrix1 = solution.generateMatrix(3);
System.out.println("生成的 3x3 螺旋矩阵:");
for (int i = 0; i < matrix1.length; i++) {
for (int j = 0; j < matrix1[0].length; j++) {
System.out.print(matrix1[i][j] + " ");
}
System.out.println();
}
System.out.println();
}
}