zl程序教程

您现在的位置是:首页 >  .Net

当前栏目

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

2023-02-18 16:35:25 时间

题目:

思路:

【1】思路一:毫无头绪之下,其实暴力破解,采用双循环也是不错的,毕竟做出来总比没做出来的要好。

【2】思路二:一看到有序,其实可以考虑二分查找,基于暴力破解的基础上,对每一行的遍历采用二分查找来进行优化减少遍历次数。【但是其实还是不够优化】

【3】思路三:其实有考虑过对角线,进行二分查找,但是对角线会存在一个问题,其实就是只能筛选出部分数据,如数据示例:

//每一行的数据
参数:[1, 10, 16, 26, 27, 34]
参数:[8, 13, 26, 32, 41, 42]
参数:[17, 25, 29, 35, 44, 46]
参数:[20, 33, 38, 47, 49, 50]
参数:[23, 39, 40, 50, 54, 58]
参数target:42

【4】思路四:而是二分查找,不过是基于他这种结构和图十分相像,可以按图的方式进行遍历,次数最少,而且不用开辟过多的空间。如图:

 

代码展示:

public class Offer {
    public static void main(String[] args) {
        Random random = new Random();
        int target = random.nextInt(100)+1;
        int n = random.nextInt(10)+1;
        int m = random.nextInt(10)+1;
        int[][] arr = new int[n][m];
        int target1,target2;
        for (int i=0;i<n;i++){
            if (i == 0){
                target1 = random.nextInt(10)+1;
            }else {
                target1 = arr[i-1][0]+ random.nextInt(10)+1;
            }
            for (int j=0;j<m;j++){
                if (j == 0){
                    arr[i][j] = target1;
                }else {
                    if (i!=0&&j!=0){
                        target2 = Math.max(arr[i][j-1],arr[i-1][j]);
                    }else {
                        target2 = arr[i][j-1];
                    }
                    arr[i][j] = target2 + random.nextInt(10)+1;
                }
            }
        }
        for (int i=0;i<n;i++){
            System.out.println("参数:"+ Arrays.toString(arr[i]));
        }
        System.out.println("参数target:"+ target);
        System.out.println(Method2(arr,target));
//        System.out.println(Method2(getArr(),target));
    }

    public static int[][] getArr(){
        int[][] arr = new int[5][5];
        arr[0] = new int[]{1,   4,  7, 11, 15};
        arr[1] = new int[]{2,   5,  8, 12, 19};
        arr[2] = new int[]{3,   6,  9, 16, 22};
        arr[3] = new int[]{10, 13, 14, 17, 24};
        arr[4] = new int[]{18, 21, 23, 26, 30};
        for (int i=0;i<5;i++){
            System.out.println("参数:"+ Arrays.toString(arr[i]));
        }
        return arr;
    }

    //思路一:双循环暴力破解,但是这种其实并不可取,但是如果实在想不出,却也是一条路子
    public static boolean Method1(int[][] matrix, int target){
        boolean result = false;
        if (matrix == null) {
            return result;
        }
       for (int[] matrixTemp : matrix){
           for (int temp : matrixTemp){
               if (temp == target){
                   result = true;
               }
           }
       }

        return result;
    }

    //思路二:二分查找,简单点的就是对暴力破解的方法进行优化,因为每一行都是有序的,所以每一行都可以使用二分查找简化时间
    public static boolean Method2(int[][] matrix, int target){
        if (matrix.length == 1 && matrix[0].length == 0){
            return false;
        }
        //对每一行进行遍历
        for (int i = 0; i < matrix.length; i++){
            int left = 0, right = matrix[i].length - 1;
            //进行二分查找
            while (left < right){
                int mid = (right + left)/2;
                if (matrix[i][mid] < target){
                    left = mid + 1;
                }else {
                    right = mid;
                }
            }
            if (matrix[i][left] == target){
                return true;
            }
            //如果当前行没有找到目标且下一行的第一个元素已经大于目标值,则无目标值,数组规模大时可以提高效率
            if (i < matrix.length - 1 && matrix[i + 1][0] > target) {
                return false;
            }
        }
        return false;
    }

    //思路三:二分查找,这种就比较抽象,其实把数据数组向左旋转45度,会发现有点像图的结构
    //而且有规律是的往左走是减,往右走是加。所以按图的方式遍历的话可以省略掉遍历很多数据的功夫
    //0 ms 击败 100%
    //内存 47.3 MB 击败 55.67%
    //这种无疑是最好的,因为遍历少,开辟的额外空间也少
    public static boolean Method3(int[][] matrix, int target){
        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;
    }
}