二维数组2:螺旋矩阵两道题
Posted 纵横千里,捭阖四方
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了二维数组2:螺旋矩阵两道题相关的知识,希望对你有一定的参考价值。
二维数组的特性决定了它可以有一种非常有意思的题:可以顺时针或者逆时针转圈访问数组,可以从外围到中心,也可以从中心到外围(该类型还没见过)。这种题在leetcode中还不止一道,其难点是如何准确判定打印时上下左右四个角的位置,只要位置定了,每条边的访问就是一个简单的线性遍历了。我们就来看一下具体怎么处理吧。
一.leetcode54 顺时针打印二维数组
给定一个m x n大小的矩阵,按螺旋的顺序返回矩阵中的所有元素。
这个题思路很简单,就是转圈打印,但是写正确并且能执行却很难。如果你能一把写对,面试官一定会喜欢你。这里的主要问题是一堆边界条件的判断。为了方便处理,对于上下左右四个位置,我们添加四个标志点,这样正式循环的时候只考虑四个标志位之间的问题就行了。否则写几行循环自己都无法确定循环到哪里结束,想死的冲动都有:
int top=0,bottom=matrix.length-1;
int left=0,right=matrix[0].length-1;
那么接下来,就是调整这四个参数,调整一次,打印一圈,直到最终结束。于是我们就很容易构造出了代码框架:
public ArrayList<Integer> spiralOrder(int[][] matrix) {
ArrayList<Integer> res=new ArrayList<>();
if(matrix.length==0){
return res;
}
//定义四个坐标,表示每次循环的四个位置
int top=0,bottom=matrix.length-1;
int left=0,right=matrix[0].length-1;
while(??){//①
for(int i=??;i??;i++){//②
res.add(matrix[top][i]);
}
for(int i=??;i??;i++){//③
res.add(matrix[i][right]);
}
for(int i= ??;i??;i--){//④
res.add(matrix[bottom][i]);
}
for(int i=??;i??;i--){//⑤
res.add(matrix[i][left]);
}
top++;
bottom--;
left++;
right--;
}
return res;
}
好了,代码的框架就是这样子,接下来将上面五个标记序号的位置填上对应的条件,这里一共有9处,有的还需要使用&&,只要你一个写错,执行就会挂掉,请问你该怎么办?
我们逐步分析:
1.什么时候循环该结束
如果想不通,我们可以画个简单的图:
在这里,很明显看到,每循环一圈,两侧都会减少一行,所以每个变量走到中间位置就行了。那中间应该是什么呢?先看列的,这里应该是要到第二行4的位置,因为(matrix.length+1)/2=2,所以应该有left<2 。那列还需要考虑吗?肯定的,因为要保证行能到第二列,因为(matrix[0].length+1)/2=1,所以此时应该有right<(matrix[0].length+1)/2.不放心,再画两种情况看一下:
如果还不放心,可以再画一个4x4的。最后:应该将两个并还是交呢?这里如果是并的话,如果left要停止了,但是right还能继续循环,那么此时就不应该循环了,所以肯定是与。如果不放心,再画一个差异大点的矩阵,例如5X8,会发现一端结束的时候,另外一端要么只剩一行,此时循环里的一路遍历就打印出来了,要么就没有了,所以此时应该将其与起来。
所以我们在①处的条件为top<(matrix.length+1)/2 &&left<(matrix[0].length+1)/2.
1.2 ②的条件是什么
此时肯定从i=left开始循环的,问题不大。那结束条件呢?应该是遇到right的时候,那该小于还是小于相等呢?还是画图来看
这里很明显left=0,right=2,循环的时候应该是要能访问3的,所以条件就是i<=right。
如果不放心,就再画个其他图看看。
1.3 ③的条件是什么
从上面图里可以看到,是要同3之后的6开始循环到9.所以我们可以放心的写起止条件为:i=top+1,结束条件似乎i<=bottom,其他推论一样。
1.4 ④的条件是什么
很明显,这里是从8到7的,所以开始条件好判断:i=right-1,那结束条件呢?7的时候i=left,所以有i>=left,那这就结束了吗?这里还要top<bottom,top一直在++,bottom一直在–,不过不保证top<bottom,就可能继续循环打印了。灵魂之问又来了,这里top能不能等于bottm?答案是不能,为什么呢?还是以3X3为例,经过上面的分析,可以简化为:while(top<2&&right<2){for(){}。。。。}可以看到while是会循环0 和1 两次的,如果上面是等于的话,top==bottom的时候就会多循环一次,因此不行。⑤的位置和上面类似,所以我们最终的代码是:
public ArrayList<Integer> spiralOrder(int[][] matrix) {
ArrayList<Integer> res=new ArrayList<>();
if(matrix.length==0){
return res;
}
//定义四个坐标,表示每次循环的四个位置
int top=0,bottom=matrix.length-1;
int left=0,right=matrix[0].length-1;
while(top<(matrix.length+1)/2 &&left<(matrix[0].length+1)/2){
for(int i=left;i<=right;i++){
res.add(matrix[top][i]);
}
for(int i=top+1;i<=bottom;i++){
res.add(matrix[i][right]);
}
for(int i= right-1;i>=left&& top!=bottom;i--){
res.add(matrix[bottom][i]);
}
for(int i=bottom-1;i>=(top+1)&&left!=right;i--){
res.add(matrix[i][left]);
}
top++;
bottom--;
left++;
right--;
}
return res;
}
二.59. 螺旋矩阵 II
给你一个正整数 n
,生成一个包含 1
到 n2
所有元素,且元素按顺时针顺序螺旋排列的 n x n
正方形矩阵 matrix
。
示例1:
输入:n = 3
输出:[[1,2,3],[8,9,4],[7,6,5]]
示例2:
输入:n = 1
输出:[[1]]
这个题有多种思路,我自己实现的也有些复杂,我找到的最简洁的实现是这样的:
生成一个 n×n 空矩阵 mat,随后模拟整个向内环绕的填入过程:
(1)定义当前左右上下边界 l,r,t,b,初始值 num = 1,迭代终止值 tar = n * n;
(2)当 num <= tar 时,始终按照 从左到右 从上到下 从右到左 从下到上 填入顺序循环,每次填入后:
执行 num += 1:得到下一个需要填入的数字;
更新边界:例如从左到右填完后,上边界 t += 1,相当于上边界向内缩 1。
(3)使用num <= tar而不是l < r || t < b作为迭代条件,是为了解决当n为奇数时,矩阵中心数字无法在迭代过程中被填充的问题。
最终返回 mat 即可。
图示是这样的:
代码:
class Solution {
public int[][] generateMatrix(int n) {
int l = 0, r = n - 1, t = 0, b = n - 1;
int[][] mat = new int[n][n];
int num = 1, tar = n * n;
while(num <= tar){
for(int i = l; i <= r; i++) mat[t][i] = num++; // left to right.
t++;
for(int i = t; i <= b; i++) mat[i][r] = num++; // top to bottom.
r--;
for(int i = r; i >= l; i--) mat[b][i] = num++; // right to left.
b--;
for(int i = b; i >= t; i--) mat[i][l] = num++; // bottom to top.
l++;
}
return mat;
}
}
以上是关于二维数组2:螺旋矩阵两道题的主要内容,如果未能解决你的问题,请参考以下文章