题目

在一个 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

限制:
0 <= n <= 1000
0 <= m <= 1000

注意:本题与主站 240 题相同:https://leetcode-cn.com/problems/search-a-2d-matrix-ii/

解题思路

1、暴力破解

如果不考虑二维数组排好序的特点,则直接遍历整个二维数组的每一个元素,判断目标值是否在二维数组中存在。
依次遍历二维数组的每一行和每一列。如果找到一个元素等于目标值,则返回 true。如果遍历完毕仍未找到等于目标值的元素,则返回 false。

复杂度分析**

  • 时间复杂度:O(n*m)。二维数组中的每个元素都被遍历,因此时间复杂度为二维数组的大小。
  • 空间复杂度:O(1)。

2、从右下角起始遍历(自己写的)

行倒序+列倒序遍历二维数组,配合行折半与列折半查找

已知每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。

可以推断出:

  • 如果 target 小于第 i 的首个数字,说明 target 值肯定不会在第 i 行中出现
  • 如果 target 大于第 i 行的末个数字,说明 target 值肯定不会在第 i 行中出现

复杂度分析

  • 时间复杂度:O(n * m )。
  • 空间复杂度:O(1)。

3、从右上角开始遍历(最优解)

如下图所示,我们将矩阵逆时针旋转 45° ,并将其转化为图形式,发现其类似于 二叉搜索树 ,即对于每个元素,其左分支元素更小、右分支元素更大。因此,通过从 “根节点” 开始搜索,遇到比 target 大的元素就向左,反之向右,即可找到目标值 target 。
04. 二维数组中的查找 - 图1

从矩阵 matrix 左下角元素(索引设为 (i, j) )开始遍历,并与目标值对比:

  • matrix[i][j] > target 时,执行 i-- ,即消去第 i 行元素;
  • matrix[i][j] < target 时,执行 j++ ,即消去第 j 列元素;
  • matrix[i][j] = target 时,返回 true ,代表找到目标值。

若行索引或列索引越界,则代表矩阵中无目标值,返回 false 。

复杂度分析:
  • 时间复杂度 O(n + m):其中,n 和 m 分别为矩阵行数和列数,此算法最多循环 n + m次。
  • 空间复杂度 O(1) : i, j 指针使用常数大小额外空间。

实现

1、暴力破解

  1. class Solution {
  2. public boolean findNumberIn2DArray(int[][] matrix, int target) {
  3. // 判空
  4. if (matrix == null || matrix.length == 0 || matrix[0].length == 0) {
  5. return false;
  6. }
  7. // 获取行列数
  8. int rows = matrix.length, columns = matrix[0].length;
  9. // 遍历
  10. for (int i = 0; i < rows; i++) {
  11. for (int j = 0; j < columns; j++) {
  12. if (matrix[i][j] == target) {
  13. return true;
  14. }
  15. }
  16. }
  17. return false;
  18. }
  19. }

image.png

2、从右下角起始遍历

class Solution {
    public boolean findNumberIn2DArray(int[][] matrix, int target) {
        if (matrix == null || matrix.length == 0 || matrix[0].length == 0) {
            return false;
        }

        //矩阵行数
        int rows = matrix.length;
        //矩阵列数
        int columns = matrix[0].length;

        // 倒序遍历行
        for (int i = rows - 1; i >= 0; i--) {
            // 行折半查找
            if (target < matrix[i / 2][0]) {
                i = i / 2;
                continue;
            }

            // 如果 target 小于当前行的首个数字,说明 target 值肯定不会在第 i 行中出现
            if (target < matrix[i][0]) {
                continue;
            }

            // 如果 target 大于当前行的末个数字,说明 target 值肯定不会在第 i 行中出现
            if (target > matrix[i][columns - 1]) {
                continue;
            }

            // 倒序遍历列
            for (int j = columns - 1; j >= 0; j--) {
                // 列折半查找
                if (matrix[i][j / 2] > target) {
                    j = j / 2;
                    continue;
                }

                if (matrix[i][j] == target) {
                    return true;
                }
            }
        }

        return false;
    }
}

image.png

3、从右上角开始遍历

class Solution {
    public boolean findNumberIn2DArray(int[][] matrix, int target) {
        // 默认为二维数组右上角的坐标
        int i = matrix.length - 1;
        int 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;
    }
}

image.png