「剑指Offer 04.二维数组中的查找」

131 阅读2分钟

「剑指Offer 04.二维数组中的查找」

Offer 驾到,掘友接招!我正在参与2022春招打卡活动,点击查看活动详情

题目描述(level 中等)

在一个 n * m 的二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个高效的函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。

示例
现有矩阵 matrix 如下:
[
  [1,   4,  7, 11, 15],
  [2,   5,  8, 12, 19],
  [3,   6,  9, 16, 22],
  [10, 13, 14, 17, 24],
  [18, 21, 23, 26, 30]
]
给定 target = 5,返回 true。
给定 target = 20,返回 false
思路分析

结合题意,二维数组matrix[n][m]每一行都是递增的,每一列都是递增的,可以得出在矩阵中数字最小值在左上角;而最大值在右下角。分别使用i,j表示行、列坐标。其中i的取值范围为[0,n)j的取值范围为[0, m)。那么如何某一列或者某一行呢?将矩阵展开如下

[[1,   4,  7, 11, 15], [2,   5,  8, 12, 19], [3,   6,  9, 16, 22], [10, 13, 14, 17, 24], [18, 21, 23, 26, 30]]

第一行[1, 4, 7, 11, 15]就可以表示为:matrix[0][j] 0 <= j < m。而与之对应的第一列则可以表示为:matrix[i][0] 0 <= i < n。单独看某一行,如果matrix[i][j] > target则可以排除下方的一整行,即i--。同样的如果matrix[i][j] < target则可以一列,逐渐往右侧逼近,j++

1471115
2581219
3691622
1013141724
1821232630

假设target = 9,按照整行整列的消除(从左下角开始遍历),即从“18”开始遍历整个矩阵,现在明确的是18>9而在18所在的行中,其为最小值,所以可以直接跳过这一行,即i--,同理倒数第二行也可以消除。当遍历到第三行时,需要知道的是,下面两行已经被排除了,不会在以后的遍历过程中走到,其实此时的表格已经是三行五列了。而从左往右是递增的,从上往下也是递增的,可以得出结论3是其所在列中的最大值(下面两行已经被排除),3<9一定可以得出此列没有比9还大的数字,故直接排除,执行j++在下一列中寻找。思路总结如下:

  • 从左下脚开始遍历 i,j。
  • 从左往右看:数字大小逐渐递增,单独一行看左侧的数字是最小的,遍历时如果有 matrix[i][j] > target,则可以排除一行,也即 i--;
  • matrix[i][j] < target 则可以消除一整列,即j++,
  • matrix[i][j] == target 返回true
代码实现
class Solution {
  public static boolean findNumberIn2DArray(int[][] matrix, int target) {
    if (null == matrix || matrix.length < 1) {
      return false;
    }
    int i = matrix.length - 1, j = 0;
    while (i >= 0 && j < matrix[0].length) {
      if (matrix[i][j] > target) {
        i--;
      } else if (matrix[i][j] < target) {
        j++;
      } else {
        return true;
      }
    }
    return false;
  }
}
复杂度

时间复杂度O(N+M):即使循环遍历整个矩阵,时间复杂度为N+M,扫描到每一个数字。

空间复杂度O(1):未使用额外的内存空间。

链接

二维数组中的查找