[剑指offer刷题04] - 二维数组中的查找

题目(题目及部分解法来源于力扣)

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

solution one:

  • 双重for循环遍历每个元素,判断数组是否含有该数组。但这种方法基本忽略题目所给信息。

solution two:

  • 题目中说:每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。这句话怎么理解?
  • 可以这样看:给定整数target,假设当前搜索到a行b列,如果 target < arr[a][b+1] ,说明b列后面的数字都要大于target。此时可以停止搜索所在行。此时在意a行b列作为起点,搜索b列中是否有target,但也需要判断,如果arr[a + 1][b] > target ,就说明所在列后面都大于target,就不用继续遍历所在列了。
 public static boolean findNumberIn2DArray1(int[][] matrix, int target) {
         if (matrix == null || matrix.length == 0 || matrix[0].length == 0) {  //先判断数组是否为空
            return false;
        }
        int rows = matrix.length;
        int cols = matrix[0].length;
        for(int i = 0 ; i < rows; ++i)
        {
            for(int j = 0 ; j < cols; ++j)
            {
                if(matrix[i][j] == target)  return true;
                if( (j + 1 == cols) || (matrix[i][j + 1] > target) )
                {
                    for(int k = i; k < rows; ++k)
                    {
                        if(matrix[k][j] == target) return true;
                       if( (k + 1 == len1) || (matrix[k+1][j] > target)) break;   (1)
                    }
                }
            }
        }
        return false;
    }

下面这部分是为了复习[剑指offer刷题03]中学过HashSet,solution two和[剑指offer刷题03]中学过HashSet都懂得同学,可直接看solution three.
如果去掉solution two中语句(1)就会有个漏洞,就是可能会重复搜索所在列。例如:下面这个矩阵,如果搜索18的话,按照上面的思路,由于第二行19大于18,所以会搜索第4列的12、16、17、26。而在第三行,22大于18,会搜索第四列剩余元素16、17、26。这样就造成重复搜索。
所以我们在下面的方法程序中利用[剑指offer刷题03]中学过HashSet。由于HashSet中的元素不会重复,所以只要将访问过的列放入HashSet中后,下一次判断要循环的列是否在HashSet中即可。
在这里插入图片描述

  • 改进代码:
    public static boolean findNumberIn2DArray2(int[][] matrix, int target) {
        HashSet<Integer> sets = new HashSet<Integer>();  // 建立HashSet集合
		if (matrix == null || matrix.length == 0 || matrix[0].length == 0) {
            return false;
        }
        int len1 = matrix.length;
        int len2 = matrix[0].length;
        for(int i = 0 ; i < len1; ++i)
        {
            for(int j = 0 ; j < len2; ++j)
            {
                if(matrix[i][j] == target)  return true;
                if( (j + 1 == len2) || (matrix[i][j + 1] > target) )
                {
                    if(sets.contains(j)) continue;  //判断HashSet中是否包含所在列
                    for(int k = i; k < len1; ++k)
                    {
                        if(matrix[k][j] == target) return true;
                    }
                    sets.add(j); //如果不包含target,则将遍历过的列加入到HashSet中
                }
            }
        }
        return false;
    }

solution three:

  • 在solution two中,我们的方法是从二维数组左上角进行遍历的。换个角度,如果我们在二维数组右上角进行遍历的话,这样也是一种方法。如果当前元素大于目标值,说明当前元素的下边的所有元素都一定大于目标值,因此往下查找不可能找到目标值,往左查找可能找到目标值。如果当前元素小于目标值,说明当前元素的左边的所有元素都一定小于目标值,因此往左查找不可能找到目标值,往下查找可能找到目标值。
public boolean findNumberIn2DArray(int[][] matrix, int target) {
        if (matrix == null || matrix.length == 0 || matrix[0].length == 0) {
            return false;
        }
        int rows = matrix.length, columns = matrix[0].length;
        int row = 0, column = columns - 1;
        while (row < rows && column >= 0) {
            int num = matrix[row][column];
            if (num == target) {
                return true;
            } else if (num > target) {
                column--;
            } else {
                row++;
            }
        }
        return false;
    }

summary:相比solution two, solution three要简便的多,且代码看起来更整洁。所以有时候从反向的角度看待同一个问题,会有不同的结果,还是要学会让自己多角度看待问题。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值