问题描述
在一个二维数组中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。
思路
- 不知道问题规模如何。这题可以用简单的暴力法。(复杂度
O(n*m)
) - 也可以对一维数组进行顺序查找,对二维数组进行二分查找。(复杂度
O(n*log(m))
) - 也可以对一维数组进行2次二分查找确定二维数组的查找范围,然后对二维数组进行二分查找。(复杂度
kO(n*log(n)*log(m))
) 具体怎么做呢?我们可以知道,二维矩阵最右面那一列,是所在行中最大的值。如果这个最大值都比target小,那么就没必要查找了。我们根据这个可以得到最大值的上边界。 拿到这个边界之后,二维矩阵就被我们切割成了比较符合条件的矩阵。 然后我们再如法炮制,找到最小值的下边界。(最小值都比target大的,也不用找了)最后再用二分查找来找符合条件的行。
其中k是个跟n相关的数。这样看复杂度还不如方法二,但是效率要比方法2要高。因为方法二中对一维数组的顺序查找并不会对不满足条件的二维数组进行二分查找,但是要对每个一维数组都检查一遍。
本方法跳过了很多根本就不满足的,少比较了好多次。不过这样写起来有难度。如果是面试,建议写出方法二之后,口述方法三,如果面试官让写方法三的代码,再写也不迟。
方法一
public boolean Find(int target, int [][] array) {
for(int[] arr:array){
for(int tmp:arr){
if(tmp == target) return true;
}
}
return false;
}
方法二
public class Solution {
public boolean Find(int target, int [][] array) {
if(array.length == 0 || array[0].length == 0) return false;
int height = array.length;
int width = array[0].length;
for(int i = 0; i < height; i++){
if(array[i][0] > target || array[i][width-1] < target){
continue;
}
int left = 0, right = width-1, mid = (left + right)/2;
while(left <= right){
mid = (left + right)/2;
if(array[i][mid] == target) return true;
else if(array[i][mid] < target) left = mid + 1;
else right = mid - 1;
}
}
return false;
}
}
方法三
class Solution {
public boolean Find(int target, int [][] array){
if(array.length == 0 || array[0].length == 0) return false;
int height = array.length, width = array[0].length;
int left = 0,right = height-1;
int mid = (left+right)/2;
while(left <= right){
mid = (left+right)/2;
if(array[mid][width-1] == target){
return true;
}else if(array[mid][width-1] < target){
left = mid + 1;
}else{
right = mid - 1;
}
} // 以上是二分拿到最大值的上边界
int rangeLeft = left;
right = height-1;
while(left <= right){
mid = (left+right)/2;
if(array[mid][0] == target){
return true;
}else if(array[mid][0] < target){
left = mid + 1;
}else{
right = mid - 1;
}
}
// 以上是二分拿到最小值的下边界
int rangeRight = right;
for(int i = rangeLeft; i <= rangeRight; i++){
left = 0;
right = width-1;
mid = (left + right)/2;
while(left <= right){
mid = (left + right)/2;
if(array[i][mid] == target){
return true;
}else if(array[i][mid] < target){
left = mid + 1;
}else{
right = mid - 1;
}
}
}// 以上是真正的二分
return false;
}
}