跳到主要内容

498. 对角线遍历

1. 题目简介

难度:中等,原题链接:498. 对角线遍历

给你一个大小为 m x n 的矩阵 mat ,请以对角线遍历的顺序,用一个数组返回这个矩阵中的所有元素。

2. 问题分析

一道模拟的题目,只要能够模拟出对角线遍历的过程,就可以解出这道题目。

为了简化遍历的复杂度,我们需要先找到对角线遍历的规律。

2.1. 对角线遍历转化为层级遍历

如果把每一个对角线都当作一层来看的话,那么这道题目就变成了找到每一层遍历的起点与方向

按照这一思路,继续观察每一层级的节点,可以发现层级的开始或者结束都会落在最左一列最下一行,因此可以计算出总的层级数量是 (m - 1) + (n - 1)

继续观察每一个层级,会发现,每一层的横纵坐标和总是与所在的层级数相同,也就是对于第 i 层的起点 start[x][y],有 x + y = i。比如:

  1. 第 0 层只有 [0, 0] 这一个点, 横纵坐标和为 0
  2. 第 1 层有 [0, 1][1, 0] 两个点, 横纵坐标和为 1
  3. 以此类推

2.2. 遍历每一个层级

接下来我们要遍历每一个层级。首要问题就是找到遍历的起点与终点。

为了简化遍历的复杂度,我们可以先假设所有层级的遍历都是从左下到右上的。那么所有的起点一定会落在最左一列最下一行

对于第 i 层的起点 start[x][y],它所在的位置有两种情况:

  1. 起点在最左侧的一列,也就是 y = 0,那么起点为 [i, 0]
  2. 起点在最下侧的一行,也就是 x = m - 1,那么起点为 [m - 1, i - (m - 1)]

这主要取决于起点是落在最左一列还是最下一行。也就是说,如果 i < m,那么起点为 [i, 0],否则起点为 [m - 1, i - (m - 1)]

可以确定起点的横坐标为 start[x] = Math.min(i, m - 1),即到达最左下角的位置时,剩下层级的起点会沿着最下一行向右侧移动。

同样的思路我们也可以找到终点的横坐标 end[x] = i - Math.min(i, n - 1)

2.3. 调整遍历顺序

在上一步中,为了简化遍历的复杂度,我们假设了所有层级的遍历都是从左下到右上的。

接下来要模拟真实的遍历规则了。

可以发现,层级遍历的方向与层级数奇偶性有关。如果从第 0 层开始排序的话,那么对于层数 t 来说:

  1. t % 2 == 0 时,遍历方向为左下右上
  2. t % 2 == 1 时,遍历方向为右上左下

因此,我们可以根据层级数 t 的奇偶性来调整遍历的方向。

3. 代码实现

class Solution {
public int[] findDiagonalOrder(int[][] mat) {
int m = mat.length;
int n = mat[0].length;
int[] res = new int[m * n];
int count = 0;
int t = (m - 1) + (n - 1);
for (int i = 0; i <= t; i++) {
int startX = Math.min(i, m - 1);
int endX = i - Math.min(i, n - 1);
if (i % 2 == 0) {
for (int j = startX; j >= endX; j--) {
res[count++] = mat[j][i - j];
}
} else {
for (int j = endX; j <= startX; j++) {
res[count++] = mat[j][i - j];
}
}
}
return res;
}

public static void main(String[] args) {
int[][] mat = new int[][] { { 1, 2, 3 }, { 4, 5, 6 }, { 7, 8, 9 } };
Solution s = new Solution();
int[] res = s.findDiagonalOrder(mat);
System.out.println(res);
}
}

4. 复杂度分析

  • 时间复杂度:O(n),每个元素都遍历了一遍。
  • 空间复杂度:O(1),没有使用额外的空间。

5. 关联问题

矩阵的各种遍历方式:

  1. 54. 螺旋矩阵
  2. 59. 螺旋矩阵 II
  3. 498. 对角线遍历