一、问题描述
Category | Difficulty | Likes | Dislikes |
---|---|---|---|
algorithms | Medium (77.37%) | 618 | - |
给你一个正整数 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
二、xin麒的思路
1、摘要:
该题主要是涉及边界的问题,同时考察对编程的熟练程度(占比也不大),本题应用到作者的方法论有:观察法(通过观察得到规律)、特定代入法(使用几个典型的模型代入,构造出代入的模型的解决框架,再详细分析完善各种特殊的情况)
2、前提引入:
- 每次都将num变量赋值给遍历到的数组元素,并且每次num都会自增;
- 以
end = n
和start = 0
分别作两边界的初始化; - 第**(first + 1)行 第(second + 1)列的数组元素为nums[first][second]**;
3、分析:
通过观察,发现矩阵有三个转折点,分别为左上角,右下角,左下角,我们知道,那么状态改变时(进行转折)这三处很有可能会做出一些变化。我们可以取当n=3的情况进行分析:
- 3.1.1指针一直往左走:当在第一行遍历时,
second
不断增加,直到second
等于end - 1
时,便达到临界状态,那么循环退出条件必然包括second < end
;
- 停顿下思考 --> 需要将
second
调整为end - 1
,保持second
在最后一列;同时我们希望下一次修改数组元素时是可以在下一个元素开始,于是需要将first + 1
;
- 3.1.2指针一直往下走:first不断增加,当向下走到尽头时,便是first等于end-1时的临界状态,循环退出条件必然包括
first < end
;
- 我们停顿下思考 --> 我们需要将
first
保持为end - 1
,同时我们希望下一次修改数组元素时是可以在下一个元素开始,于是需要将second - 1
;另外考虑到如果再有1向下走的情况,那么end应该是需要减小了,于是需要end - 1
;
- 3.1.3指针一直往左走,当向左时,
second
不断减小,当减小到second
等于start
时,便到达临界状态。循环退出条件必然包括second >= start
;
- 停顿下思考 --> 我们希望接下来可以向上转折,那么需要将
second
保持为start
;同时我们希望下一次修改数组元素时是可以在下一个元素开始,于是需要将first - 1
;
- 因为向左走时,该层循环左边界start等于0,那么下次继续左走时,左边界应该向右收索,那么需要
start+1
(也因为向上走时上边界是start+1
,这样修改也符合);
-
3.1.4当一直向上走,当走到坐标(1,0)的位置,修改好元素时便退出,那么循环退出条件必然包括
first >= start
; -
3.2此后外部循环继续,重新定义
first = start
,second = start
,也就是说每一次外层循环开始时都是从左上角到右下角的对角线对应的元素开始的,符合; -
3.3外层循环的退出条件的思考:我们可以看数组元素的不断修改的过程,如果数组的最后一个数被修改了,会是什么情况呢?是数组元素被值为n*n的num变量赋值了。但是由于num是一个不断增长的数,那么我们可以使用
num != n*n + 1
来作为最外层的循环的循环条件
- 而内层有4个并行的循环,防止num为
n*n + 1
时会进入这些内层循环,于是再在内层循环条件里并上一个和外层循环一样的条件。
三、xin麒的题解
class Solution {
public int[][] generateMatrix(int n) {
int num = 1;
int end = n;
int start = 0;
int[][] nums = new int[n][n];
int finalnum = n * n + 1;
int first = 0;
int second = 0;
while (num != finalnum){
first = start;
second = start;
while (second < end && num != finalnum){//往右
nums[first][second++] = num++;
}
second--;//上述循环second退出了边界,需要补回
first++;
while (first < end && num != finalnum){//往下
nums[first++][second] = num++;
}
first--;//上述循环first退出了边界,需要补回
second--;
end--;//这时右边界向左收缩
while (second >= start && num != finalnum){//往左
nums[first][second--] = num++;
}
second++;//上述循环second退出了边界,需要补回
start++;//左边界向右收缩
first--;
while (first >= start && num != finalnum){//往上
nums[first--][second] = num++;
}
}
return nums;
}
}