题目描述:在一个二维数组中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组 array 和一个整数 target,判断数组中是否含有该整数。
测试用数组: 假定行数为row,列数为col
- 空数组 : int[][] arr1 = { {} };
- 非空数组:int[][] arr2 = {{1,2,3,4},{5,6,7,8},{9,10,11,12}},如下图
row/col | 0 | 1 | 2 | 3 |
---|---|---|---|---|
0 | 1 | 2 | 3 | 4 |
1 | 5 | 6 | 7 | 8 |
2 | 9 | 10 | 11 | 12 |
解题思路1:第一个想法是把二维数组看成一维的,然后遍历整个数组,这样子查找花的时间与该整数所在的一维数组下标index有关,如果存在,整个算法循环比较index次得到结果,如果不存在,算法达到最坏时间复杂度,为O(n)
结论:该思路因为没有运用到题目给的二维数组的递增特性,肯定不是最佳的解决方法,不提倡使用。
解题思路2:将该二维数组的每一行看成一个有序递增的一维数组,利用二分查找,查找每一行得到答案,由于查找一行的时间复杂度为O(log(col)),所以此算法的时间复杂度为O(row*log(col))
//方法的作用:返回该整数相应的数组下标,找不到则返回负数
public static int[] Find1(int target, int[][] array) {
//边界条件判断
if(array[0].length == 0){
//数组不存在元素
return null;
}
//定义数组用来存放该整数所在的数组下标
int res[] = {-1, -1}; //-1代表不存在
//遍历比较过程
for(int i = 0; i < array.length; i++) {
int low = 0;
int high = array[0].length-1;
System.out.println("行数和列数分别为:"+low+" and "+high);
while(low<=high) { //对单行二分查找过程
int mid = (low+high)/2;
if(target>array[i][mid])
low = mid + 1;
else if(target<array[i][mid])
high = mid - 1;
else{
p[0] = i; //将行数赋值给p[0]
p[1] = mid; //将列数赋值给p[1]
break;
}
}
}
return p;
}
测试结果:
- 空数组的测试情况
- 非空数组的测试情况
结论:二分查找要求查找表中的元素已经有序,刚好该二维数组每一行的特性都符合,而且该算法本身的特点就是查找效率高,需要熟练掌握,提倡使用。
解题思路3:由该二维数组的特性,可选取右上角或左下角的元素 array[row][col] 与 target 比较。
- target < array[row][col] 时,target 必定出现在该元素的左边,此时令列数col - 1,循环继续比较的过程;
- target > array[row][col] 时,target必定出现在元素所在列的下边,此时令行数row + 1,循环继续比较的过程.
//方法的作用:返回数组是否含有该整数
public static boolean Find(int target, int[][] array) {
//边界条件判断
if(array[0].length == 0){
//数组不存在元素
return false;
}
int row=0; //行数
int col=array[0].length-1; //列数
System.out.println("行数和列数分别为:"+row+" and "+col);
while(row<=array.length-1&&col>=0){
if(target==array[row][col])
return true;
else if(target>array[row][col])
row++;
else
col--;
}
return false;
}
测试结果:类似解题思路2的测试方法,这里不再赘述,请读者自行测试。
结论:该算法非常好的利用了该二维数组的数据特性,通过单次移动查找指标来查找整数,可以减少很多无效的比较次数,很快地找到该整数,提倡使用。
如发现错误或有其它解题思路,欢迎指正交流,一起学习,谢谢阅读。