题目描述:
在一个 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
。
问题分析:
第一种方法:暴力法,遍历二维数组,查找该二维数组中是否存在目标值target,如果存在就返回true,不存在就返回false。时间复杂度为o(n^2)
第二种方法:二分查找,由于每行是有序的,因此可以在每一行通过二分查找目标值,然后遍历所有行,时间复杂度是o(nlogn),由于每行最后一个数不是严格大于前一行的最后一个数,所以不能通过一次遍历找到目标值所在的行,故时间复杂度不能是o(nlogn)。
public class Solution {
public boolean Find(int target, int [][] array) {
if(array == null || array.length == 0 || array[0].length == 0 ) {
return false;
}
int i = 0;
for( i = 0; i < array.length; i++) {
boolean res = binarySearch(array[i],target);
if(res == true) {
return true;
}
}
return false;
}
//二分查找,查找在一个数组中是否存在目标值target
public boolean binarySearch(int[] array,int target) {
int begin = 0;
int end = array.length-1;
while(begin<=end) {
int mid = (begin+end)/2;
if(array[mid] == target) {
return true;
}else if(array[mid] < target) {
begin = mid + 1;
}else {
end = mid - 1;
}
}
return false;
}
}
第三种方法:广度优先遍历,由左上角开始逐层向外遍历,如果找到目标值直接返回true,若遍历到右下角仍未找到则返回false,但是由于该方法采用递归实现,所以部分测试用例超时。时间复杂度是o(n*m)
public class Solution {
public boolean Find(int target, int [][] array) {
if(array == null || array.length == 0 || array[0].length == 0) {
return false;
}
return search(target,array,0,0);
}
//row,col表示开始遍历的行和列
public boolean search(int target,int[][] array,int row,int col) {
if(array[row][col] == target) {
return true;
}
if(row == 0 && col == 0 && target < array[row][col]) {
return false;
}
if(row == array.length-1 && col == array[0].length-1 && target > array[row][col]{
return false;
}
boolean down = false;
boolean right = false;
if(target > array[row][col]) {
if(row+1 < array.length) {
down = search(target,array,row+1,col);
}
if(col+1 < array[0].length) {
right = search(target,array,row,col+1);
}
}
return down || right;
}
}
第四种方法:线性查找。
由于给定的二维数组具备每行从左到右递增以及每列从上到下递增的特点,当访问到一个元素时,可以排除数组中的部分元素。
从二维数组的右上角开始查找。如果当前元素等于目标值,则返回 true。如果当前元素大于目标值,则移到左边一列。如果当前元素小于目标值,则移到下边一行。
可以证明这种方法不会错过目标值。如果当前元素大于目标值,说明当前元素的下边的所有元素都一定大于目标值,因此往下查找不可能找到目标值,往左查找可能找到目标值。如果当前元素小于目标值,说明当前元素的左边的所有元素都一定小于目标值,因此往左查找不可能找到目标值,往下查找可能找到目标值。
若数组为空,返回 false
初始化行下标为 0,列下标为二维数组的列数减 1
重复下列步骤,直到行下标或列下标超出边界
获得当前下标位置的元素 num
如果 num 和 target 相等,返回 true
如果 num 大于 target,列下标减 1
如果 num 小于 target,行下标加 1
循环体执行完毕仍未找到元素等于 target ,说明不存在这样的元素,返回 false。时间复杂度为o(n+m)
class Solution {
public boolean findNumberIn2DArray(int[][] array, int target) {
if(array == null || array.length == 0 || array[0].length == 0) {
return false;
}
int row = array.length;
int col = array[0].length;
int r = 0;
int c = col-1;
while(r<row && c >=0) {
if(array[r][c] == target) {
return true;
} else if(array[r][c] < target) {
r++;
}else {
c--;
}
}
return false;
}
}