《剑指 Offer》学习记录:题 4:二维数组中的查找

题 4:二维数组中的查找

题干

在一个二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。——《剑指 Offer》P44

测试用例

  1. 二维数组中包含查找的数字(査找的数字是数组中的最大值和最小值、査找的数字介于数组中的最大值和最小值之间);
  2. 二维数组中没有查找的数字(查找的数字大于数组中的最大值、査找的数字小于数组中的最小值、査找的数字在数组的最大值和最小值之间但数组中没有这个数字);
  3. 特殊输入测试(输入空指针)。

例如有如下矩阵,给定 target = 5 返回 true,给定 target = 20,返回 false。

[
  [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]
]

解题思路

这道题可以使用蛮力法来解决,也就是直接遍历二维数组,一个一个匹配需要查找的元素,这种方法的时间复杂度为 O(n^2)。不过由于传入的矩阵并不是规则的矩阵,只要是有规则的矩阵都可能找到更优的解法。其实对于矩阵的查找,也是一个解空间缩小的过程,使用蛮力法是在一个 n × n 的解空间中每次减少 1。因此总体的思路是如何更快地缩小解空间的大小,对于矩阵来说,如果可以一次性排除多个行或多个列,就可以降低解决问题的时间复杂度。
设传入的 n × n 的矩阵中的任何一个元素是 x(i, j),其中 0 ≤ i < n 表示元素所在位置的行数,其中 0 ≤ j < n 表示元素所在位置的列数。由于矩阵的特性是“每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序”,因此从行的角度看,当任何一个元素 t < x(i, j) 时,该元素 t 必定小于元素 x(i + p, j) (i ≤ i + p < n)。同理从列的角度看,当任何一个元素 t > x(i, j) 时,该元素 t 必定大于元素 x(i, j - q) (0 ≤ j - q < j)。
这么讲其实还是很抽象,这里引用 LeetCode 上这道题的第一条热门评论,来自用户 ymy1248:

如何理解?也就是说如果把矩阵当做一个树结构,右上角的结点当做树的根结点,则满足 x(i - 1, j) < x(i + p, j) < x(i, j + 1),这恰好是一棵二叉搜素树。

把这个矩阵旋转过来,就能更明显地看出来了,当在该搜索树中没下降一层,就可以减少一行或一列的解空间。值得一提的是,也可以将矩阵左下角的元素当做根结点,此时结点的左孩子是较大的数,右节点是较小的数。

解题代码

bool findNumberIn2DArray(vector<vector<int>>& matrix, int target) {
    int idx_x = matrix.size() - 1;
    int idx_y = 0;

    while(idx_x >= 0 && idx_y < matrix[0].size())
    {
        if(matrix[idx_x][idx_y] > target) 
        {
            idx_x--;
        }
        else if(matrix[idx_x][idx_y] < target)
        {
            idx_y++;
        }
        else
        {
            return true;
        }
    }
    return false;
}

时空复杂度

按照二叉搜索树的方式扫描数组时,一次可以减少一行或一列的解空间,因此最坏情况是搜索完所有的行或列之后找不到。设矩阵是 N 行 N 列,则把每一行和每一列都遍历一遍的 T(n) = 2n,得时间复杂度 O(n)。
由于没有使用辅助空间,因此空间复杂度 O(1)。

参考资料

《剑指 Offer(第2版)》,何海涛 著,电子工业出版社
面试题04. 二维数组中的查找(标志数,清晰图解)

posted @ 2021-03-31 09:06  乌漆WhiteMoon  阅读(86)  评论(0编辑  收藏  举报