498.对角线遍历
-
简化问题,首先考虑按照逐条对角线打印元素,而不考虑翻转的情况。
-
M 行 N 列的二维矩阵总共有 M + N - 1 条对角线( 右上 -> 左下 )
-
1)如何遍历: 从左往右遍历 对角线的条数,总共遍历 M + N - 1 轮,d:[0, M + N - 1 ) 每一轮中确定当前对角线 d 的右上角的点的坐标 (x, y) , 然后 将这个点作为起始点出发,再起一个 while 循环,按照 x++, y-- 的方式,即 右上 -> 左下的方向,遍历矩阵位于该对角线上的点,并保存到结果数组中 res[i++] = matrix[x][y] , while 循环的判断条件: x < M && y >= 0 即二者都没有越界就一直循环直到越界为止。
-
2)如何折线保存: d%2 == 0 时需要 反向保存,因此这时可以搞一个 list ,然后 Collections.reverse(list) 再保存,或者,每次开始内层遍历对角线的 while 循环前记下res数组当前位置作为 start , while 循环结束时res的 i 下标就是结束位置,反转 res[start...i] 这一段区间即可(这里可以写一个 reverse 数组区间的函数)。
-
3)如何确定 对角线 d 的右上角的点的坐标: 其实所有对角线右上角的点就是整个矩阵中位于最上边一行和位于最右边一列的那些点,因此当 d < N 时,所求的点的 x 坐标都是 0 , y 坐标则是 d ,即 [ 0, d] ,而当 d >= N 时,所求的点的 x 坐标是 d - ( N - 1 ) , y 坐标全都是 N - 1 ,即 [ d - ( N - 1 ) , N - 1] 。
如下图所示:
这个题最大的难处就是首先要观察出一个二维矩阵的右对角线的条数是 M + N - 1, 因为这是我们要遍历的对角线的轮数。其次,我们要确定出每条对角线右上角的坐标(x,y),将它用对角线下标等价计算出来。(为什么呢,因为我们是对对角线进行遍历,此时拿不到每个点的二维坐标,所以需要转换)
为了看的更清楚一些,我们把矩阵中的数字都去掉:
其实最上面一行的对角线起始点坐标就是二维矩阵的第一行的坐标点,它们的横坐标都是 0,而纵坐标就是该点的二维矩阵的列下标,可以认为是(0,j),但是我们在遍历的时候,是遍历的对角线的轮数 [0, M + N - 1],而非一般的双重for循环那样遍历二维矩阵的形式(如果是那样就能拿到 i 和 j),所以第一行的对角线起始点坐标需要利用对角线的轮数下标来等价计算,它正好是 (0,d)。
同样的,最后一列的对角线起始点其实就是二维矩阵的最后一列的坐标点(i, N - 1),我们也需要用对角线的下标来等价计算出来,它是(d - (N - 1), N - 1)。
最后,在保存答案的时候,需要用到一个小小的伎俩,那就是第奇数条对角线(或者说对角线下标是d=0、2、4...的)上的值,逆序保存就可以了。也就是上面图中蓝色的对角线,应该是下面这样:
54.螺旋矩阵
-
1)按层/环访问:上右下左四条边循环访问,设4个变量分别表示上下左右边界:T = 0,B = M - 1,L = 0, R = N - 1,
-
首先在最外层使用一个while循环控制四个方向不越界: T < B && L < R ,
-
然后在每次循环中,使用4个for循环分别收集上下左右四条边上的值,在收集完后四条边同时向内收缩一圈 T++ B-- L++ R--,
-
最后跳出while循环时,只会剩下一行 T==B 或者只剩一列 L==R ,将其收集即可。
如下图所示:
例如在收集上边一行时,使用for循环遍历区间 i: [L, R),此时的横坐标固定为 T,遍历的下标是列坐标,所以for循环中每个元素的取值为matrix[T][i]。类似的,对于右边的收集 i: [T, B),元素的取值为matrix[i][R];对于下边的收集 i: [R, L),元素的取值为matrix[B][i];对于左边的收集 i: [B, T) ,元素的取值为matrix[i][L]。
最后收缩到最内层时,只会剩下一行或者只会剩下一列,如下图所示:
上面代码中有一点需要注意,最后只剩一行或只剩一列的情况,只能写成 if...else if... 的形式,不能写成两个 if 并排判断的形式。因为对于题目示例1那样的正方形矩阵,最后只会剩下一个点,此时 T==B 或者 L==R 会同时满足,如果写成两个 if 并排判断的形式,就会多收集一次这个点,导致错误答案。
-
2) DFS 遍历: 从 (0,0) 出发,按 右、下、左、上 四个方向 DFS,定义二维方向数组 int [][] dirs = { { 0 , 1 }, {1, 0}, {0, -1}, {-1, 0} } ,以及boolean[][] visited数组,每次 收集当前节点并 标记已访问 ,然后计算下一个节点 x + dirs[k][0], y + dirs[k][1] ,其中 k 表示方向,初始 k == 0 ,在每次递归中收集完节点后先判断下一个点 是否越界或者已访问 过, 如果未越界且未访问,就进行递归调用,如果 越界或者已访问过,就改变方向 k = ( k + 1 ) % 4 ,然后再次重新计算下一个节点的值(注意点)。
-
可以采用循环(循环