题目描述:
在一个二维数组中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。
解决思路:
首先假定题目中二维数组总共有m个长度为n的一维数组,想从这样一个二维数组中找到一个元素是否存在,最暴力最直接解决方式就是把二维数组遍历一遍。。它的时间复杂度为O(mn),显然这不是我们想要的结果。。
更优的解决办法:我们可以想像这个二维数组它是一个长为n,宽为m的矩阵,如下图所示:
第一种解决思路:
我们从第一行第一个元素开始找起,假如给定元素比这个元素大,那么我们应该再去拿给定元素与对这个元素右边或者下边的元素去比较,如果是跟下头的元素去比较的话,给定元素比下方元素小,那么我们应该再与下方元素的左边元素比较,依次类推。但是这样有一个问题就是,我们从矩阵的左上角或者右下角来开始找的话,没有办法去确定一个唯一的方向,拿左上角为例,如果大了,那么我们可以跟下方的或者右方的再去比较,就会有两个方向,这样就会很麻烦。。
但是,如果我们从左下角或者右上角开始的话,就可以确定一个方向了。以左下角为例,如果比当前元素小,那么我们往上找,如果比当前元素大,那么我们往右找。直至无法再找为止(如果当前元素为第一行的,比给定元素还要小的话,就要再往上找,但是没有了,说明给定元素不存在;如果说当前元素为最后一列的,不管在第几行,给定元素比它大,那么就还要往右找,但是已经没有了,这也说明给定元素不存在。)下图为寻找过程。。
这种时间复杂度为O(m+n)
代码如下(Java版):
public class Solution {
public boolean Find(int target, int [][] array) {
//排除特殊情况
if (array == null) {
return false;
}
//起始横坐标
int i = 0;
//起始纵坐标
int j = array.length - 1;
/* 如果横坐标大于该行数组的最大索引,说明还要再往右找,然而没有了,所以不存在指定元素
* 如果纵坐标小于0,说明还要往上找,然而没有了,所以不存在指定元素
* array[j] != null 是为了避免特殊情况
*/
while (j >= 0 && array[j] != null && i <= array[j].length - 1) {
if (target < array[j][i]) {
//比当前元素小往上找,纵坐标-1
j--;
} else if (target > array[j][i]) {
//比当前元素大,往右找。横坐标+1
i++;
} else {
//原当前元素相等,找到
return true;
}
}
return false;
}
}
第二种解决思路:
把每一行看成一个递增的数组,利用二分查找的思想而不是顺序遍历,可以更快地找到查找元素。
时间复杂度为O(mlogn),因为n个元素二分查找时logn,最差情况要找m次
代码如下:
public class Solution {
public boolean Find(int target, int [][] array) {
//排除特殊情况
if (array == null) {
return false;
}
//i二维数组长度即矩阵行数
for(int i = 0; i < array.length; i++){
//low为前置标位,high为后置标位
int low = 0;
//排除特殊情况,如果当前行为null,找下一行
if (array[i] == null) {
continue;
}
int high = array[i].length - 1;
//当low == high时说明已经找到了最中间的一个,也就是最后一次查找了
while(low <= high){
int mid = (low + high) / 2;
if(target < array[i][mid]) {
//如果小于应该在前半部分再找
high = mid - 1;
} else if(target > array[i][mid]) {
//如果大于应该在后半部分再找
low = mid + 1;
} else {
return true;
}
}
}
return false;
}
}
应该只有在m比n小很多时第二种方法才比第一种好。。