剑指offer- 二维数组查询数据存在

177 阅读5分钟

题目

二维数组中,每一行按照 从左到右递增 顺序排序,每一列按照 从上到下递增 顺序排序,输入一个二维数组和一个整数,判断数组中是否包含该整数

11 12 13 14 15 16
21 22 23 24 25 26
31 32 33 34 35 36
41 42 43 44 45 46
51 52 53 54 55 56

初始化二维数组

   
        int rowNumber = 5;
        int columnNumber = 6;
        int[][] arr = new int[rowNumber][columnNumber];
        for (int row = 1; row < (rowNumber + 1); row++) {
            for (int column = 1; column < (columnNumber + 1); column++) {
                arr[row - 1][column - 1] = row * 10 + column;
            }
        }
        

方案一:暴力破解

  1. 判断传入的数组是否是空或者集合长度为0,如果为0说明不存在数据,直接返回不存在

  2. 通过循环每一行数据,判断二维数组中是否存在该数据,通过需要查找的值与当前位置的值进行比较,判断下一步的动作

    • 当前位置值小于需要查询的值,继续查询

    • 当前位置值等于需要查询的值,返回存在,跳出查询

    • 当前值大于需要查询的值,数据不存在,跳出查询(二维数组从上到下,从左到右都是递增顺序排列,后面的值会比当前值更大,无查询必要)


    private static Boolean findValue(int[][] arr, int targetValue) {
        if (Objects.isNull(arr) || arr.length == 0) {
            return Boolean.FALSE;
        }


        for (int[] rows : arr) {
            for (int columnValue : rows) {
                if (columnValue == targetValue) {
                    return Boolean.TRUE;
                }

                if (columnValue < targetValue) {
                    continue;
                }
                return Boolean.FALSE;
            }
        }
        return Boolean.FALSE;
    }

优缺点

优点:方案设计简单,容易快速想到,解决问题

缺点: 算法性能不好,极端情况下需要遍历整个二维数组结构

方案二:剔除部分数据

随机选择二维数组中的一个位置,比较位置值与目标值的大小,根据比较结果缩小查询范围。二维数组如下

10 22 33 44 55 66
20 25 40 50 60 70
30 32 45 56 68 75
40 41 48 59 73 78
51 54 57 61 76 99
  1. 位置值与目标值相等,返回查询到数据
  2. 位置值大于目标值,那么目标值会存在于当前位置的上方或者左方。如目标值57,选取位置值59,那么目标值可能存在的区域为
10 22 33 44 55 66
20 25 40 50 60 70
30 32 45 56 68 75
40 41 48
51 54 57
  1. 位置值小于目标值,那么目标值会存在于当前位置的右方或下方。如目标值75,选取位置值59
55 66
60 70
68 75
73 78
61 76 99

根据剔除之后的行列数据进行查询数据是否存在

优缺点

优点:能够提出部分数据,提高效率

缺点:逻辑复杂,代码设计复杂

方案三:剔除行列法

方案二的弊端在于,只能判断出目标数据可能存在的位置,不能够完整的剔除一行或者一列数据,导致代码逻辑过于复杂

那么有没有一种方式可以有效的剔除一行或者一列数据呢?

选取点位置

  1. 对行数据而言是开始,对列数据而言是结束,即左下角点
  2. 对行数据而言是结束,对列数据而言是开始,即右上角点

这两个点的位置由于是一组数据的结束,并且是另外一组数据的开始,那么和目标点比较之后都能够有效剔除一行或者一列数据

如:二维数组如下,目标数据为32,选择右上角点作为参考点

10 22 33 44 55 66
20 25 40 50 60 70
30 32 45 56 68 75
40 41 48 59 73 78
51 54 57 61 76 99
  1. 右上角点66大于32,参考点所在列不会出现小于或等于32数据,所在列剔除,参考点 左移

    10 22 33 44 55
    20 25 40 50 60
    30 32 45 56 68
    40 41 48 59 73
    51 54 57 61 76
  2. 新右上角点位置值为55,大于32,参考点所在列不会出现小于或等于32数据,所在列剔除,参考点位置继续 左移

    10 22 33 44
    20 25 40 50
    30 32 45 56
    40 41 48 59
    51 54 57 61
  3. 新参考点值位置值44,大于32,参考点所在列不会出现小于或等于32数据,所在列剔除,参考点位置继续 左移

10 22 33
20 25 40
30 32 45
40 41 48
51 54 57
  1. 新参考点值位置值33,大于32,参考点所在列不会出现小于或等于32数据,所在列剔除,参考点位置继续***左移***
10 22
20 25
30 32
40 41
51 54
  1. 新参考点位置值为22,小于目标值32,参考点所在行不会出现大于或等于32数据,参考点位置 下移
20 25
30 32
40 41
51 54
  1. 新参考点位置值是25,小于目标值32,参考点所在行不会出现大于或等于32数据,参考点所在行剔除,坐标点 下移
30 32
40 41
51 54
  1. 新参考点值为32,与目标数据相等,查询出所要查找的数据,返回结果

终止条件

当行列循环到边界处还没有查询到数据,退出循环

    private static Boolean findValue(int[][] arr, Integer targetValue) {
        if (Objects.isNull(arr) || arr.length == 0) {
            return Boolean.FALSE;
        }

        int columnIndex = arr[0].length - 1;
        int rowIndex = 0;
        while (columnIndex >= 0 && rowIndex <= arr.length - 1) {
            int indexValue = arr[rowIndex][columnIndex];
            if (indexValue == targetValue) {
                return Boolean.TRUE;
            }

            if (indexValue > targetValue) {
                columnIndex--;
            } else {
                rowIndex++;
            }
        }
        return Boolean.FALSE;
    }