题目(题目及部分解法来源于力扣)
在一个 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要简便的多,且代码看起来更整洁。所以有时候从反向的角度看待同一个问题,会有不同的结果,还是要学会让自己多角度看待问题。