Leetcode 剑指Offer 04. 二维数组中的查找
1.题目描述
在一个 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
2.分析
方法一
由于该二维数组的特殊性,可以通过对某一个点的比较,从而节省一行一列的比较。我们很容易能够看到这个二维数组实际上是由一个一个的小的二维数组组成的,并且任何一个子二维数组都遵循着左上角最小,右下角最大的原则,那么我们就可以把这个题目转换成一个递归问题,只需要将该数组的第一列和第一行与target进行比较,如果不在,则进入去除掉第一列和第一行的二维数组重新进行比较,以此类推,直到最后找到或没有找到。
class Solution {
public boolean findNumberIn2DArray(int[][] matrix, int target) {
if(matrix.length ==0) return false;
int n =matrix.length; //行数
int m =matrix[0].length; //列数
if (n==0 || m==0){
return false;
}else {
if (target < matrix[0][0]) {
return false;
} else if (target > matrix[n - 1][m - 1]) {
return false;
} else {
for (int i = 0; i < n; i++) {
if (target == matrix[i][0]) {
return true;
}
}
for (int i = 0; i < m; i++) {
if (target == matrix[0][i]) {
return true;
}
}
if (n-1==0||m-1==0){
return false;
}else {
int[][] newMatrix = new int[n - 1][m - 1];
for (int i = 0; i < n - 1; i++) {
for (int j = 0; j < m - 1; j++) {
newMatrix[i][j] = matrix[i + 1][j + 1];
}
}
boolean numberIn2DArray = findNumberIn2DArray(newMatrix, target);
return numberIn2DArray;
}
}
}
}
}
但是经过leetcode的执行用时16ms和内存消耗41.2MB来看,这是一个非常麻烦的思路,所以我们试图寻找第二种方案。
方法二
在方法一的思想基础上进行修改,我们可以从小去见大。什么意思?我们可以把二维数组的第一个元素默认看成是最小的一个二维数组,如果target比该值小,则整个数组都要比target大,即不存在。然后再将该默认数组同时增加左后一行一列,比较target与右下角值得大小,如果target比右下角值大,则继续扩大一行一列,如果小,则就在该数组的剩余值中去寻找。简而言之,就是尽可能快的确定target在所属哪个区间中。
方法三
最优版
我们将数组逆时针旋转45°,我们能够看到此数组中间一列的数字有明显特征,即该数左边区域都比该数小,该数右边区域都比该数大。通过此情况,我们可以将该数作为标志数,用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;
3.注意点
要注意如果输入数组是 [],,要规避这个点。