目录
题目
在一个 n * m 的二维数组中,每一行都按照从左到右 非递减 的顺序排序,每一列都按照从上到下 非递减 的顺序排序。请完成一个高效的函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。
来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/er-wei-shu-zu-zhong-de-cha-zhao-lcof
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
示例:
现有矩阵 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。
来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/er-wei-shu-zu-zhong-de-cha-zhao-lcof
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
限制:
0 <= n <= 1000
0 <= m <= 1000
解题思路1——顺序比较法
在数组中查找某一个数,那么顺序比较法是绝对的宠儿,毕竟两个循环往上面一写,绝对能得到正确的答案,唯一需要担心一下的就是运行时间的限制。
不过多赘述了,这个宠儿在本题的性能表现是完全可以的,放心的用吧。
解题思路2——模糊确定查找区域
为什么是模糊地确定呢,因为暂时没想到可以精确确定范围的方法。🤣
这道题的难度是中等,就使用一个顺序比较法,可以解题,性能也够用,但终究有点给这个难度评级打了个问号的意思,况且,就执行次数来说,能缩小一些范围少做一些查找也是不错的嘛。
那么问题来了,如何缩小查找的区域?
题目中对二维数组的特征有这样的描述:
在一个 n * m 的二维数组中,每一行都按照从左到右 非递减 的顺序排序,每一列都按照从上到下 非递减 的顺序排序。
那么可以画出这个二维数组的结构:
这样一来,结合给定的二维数组的特征,可以得出这样一些结论:
这些结论貌似还看不出什么端倪,好像暂时没用,那先放在一边,接着分析。
既然数组中一个位置只能存放一个元素,那么,给数组加上一个坐标轴也没什么违和感了,此时将数组中的元素抽象成坐标系中的点。就像这样:
tip:下面提到的坐标都是抽象成点后的元素在直角坐标系中的坐标。
如果数组中某一个数大于目标值target,就如图中绿色的点所示:
那么数组的每行每列都是从小到大排列的,因此下面蓝色区域的点必然比绿色的点大:
既然绿色区域的点都比目标值大,那么比绿色区域还要大的蓝色区域内的点便更加不可能是存在目标值的区域了。连同绿色的点一起,该区域是个不可能区域:
那么剩下的粉色区域就是可能存在目标值的区域,那么接下来在粉色区域内查找便可以了,算是减少了一些比较的次数。
如果数组中某一个数小于目标值target,就如图中绿色的点所示:
那么,根据给定数组的特性,图中蓝色区域的点一定比绿色的点小:
虽然显而易见,但此时也可以确定一个不可能区域:
那么下面需要在粉色的点中接着比较。
解题思路3——精确划分查找区域
接着上面的分析。
在小于目标值时,可以确定一个不可能区域:
在大于目标值时,也可以确定一个不可能区域:
如果将这两个区域合并起来,那么剩下的区域必定是可能出现目标值的区域,并且可以确定一个很小的范围:
沿着主对角线进行比较还是比较快捷的。
那么沿着主对角线进行比较时,只有可能出现上图的两种情况。
如果目标值不在主对角线上,那么粉色的区域便是一个更为精确的查找区域,目标值可能在粉色的区域中,也可能不在粉色的区域中。
当然了,如果目标值小于主对角线的第一个元素或者是目标值大于主对角线的最后一个元素,那么都表明目标值不存在这个数组中。(之前得到的结论,整个数组的第一个元素是数组的最小值,整个数组的最后一个元素是数组的最大值)。
题解1——顺序比较法
class Solution {
public boolean findNumberIn2DArray(int[][] matrix, int target) {
if(matrix.length == 0){
return false;
}
for(int row = 0;row < matrix.length;row++){
for(int col = 0;col < matrix[row].length;col++){
if(matrix[row][col] == target){
return true;
}
}
}
return false;
}
}
题解2——模糊确定查找区域
class Solution {
public boolean findNumberIn2DArray(int[][] matrix, int target) {
int row = 0; //二维数组的行索引
int col = 0; //二维数组的列索引
boolean result = false; //搜索结果
//如果二维数组一行都没有,那么直接返回找不到
if(matrix.length == 0 || matrix[0].length == 0){
return false;
}
//如果二维数组只有一行,那么直接对该行进行横向搜索
if(matrix.length == 1){
return binarySearch(matrix,0,matrix[0].length-1,0,target);
}
//如果二维数组只有一列,那么直接对该列进行纵向搜索
if(matrix[0].length == 1){
return binarySearch(matrix,matrix.length-1,0,1,target);
}
//二维数组是一个矩阵的情况下
for(row=0;row<matrix.length && row<matrix[row].length;row++){
col = row;
if(matrix[row][col] == target){
return true;
}
if(matrix[row][col] > target){
//目标值可能位于该列的正上方
for(int i=0;i<row;i++){
result = binarySearch(matrix,i,matrix[0].length-1,0,target);
if(result == true){
return true;
}
}
for(int i=row;i<matrix.length;i++){
result = binarySearch(matrix,i,col-1,0,target);
if(result == true){
return true;
}
}
return false;
}
}
return false;
}
/**
*形参matrix:二维数组
*row,col:共同定位搜索区域
*形参mode:搜索模式,mode=1:纵向搜索;mode=0:横向搜索
*形参target:目标值
*函数功能:对二维数组指定行列进行二分查找
**/
public static boolean binarySearch(int[][] matrix,int row,int col,int mode,int target){
//横向搜索
if(mode == 0){
int left = 0;
int right = col;
while(left <= right){
int mid = (right - left)/2 + left;
if(matrix[row][mid] == target){
//找到了
return true;
}
if(matrix[row][mid] > target){
right = mid - 1;
}
if(matrix[row][mid] < target){
left = mid + 1;
}
}
return false;
}
//纵向搜索
int left = 0;
int right = row;
while(left <= right){
int mid = (right - left)/2 + left;
if(matrix[mid][col] == target){
return true;
}
if(matrix[mid][col] > target){
right = mid - 1;
}
if(matrix[mid][col] < target){
left = mid + 1;
}
}
return false;
}
}
题解3——精确确定查找范围
class Solution {
public boolean findNumberIn2DArray(int[][] matrix, int target) {
//如果矩阵是空的
if(matrix.length == 0){
return false;
}
//如果矩阵只有一行
if(matrix.length == 1){
//横向进行一次查找
for(int i=0;i<matrix[0].length;i++){
if(matrix[0][i] == target){
return true;
}
}
return false;
}
//如果矩阵只有一列
if(matrix[0].length == 1){
//纵向进行一次查找
for(int i=0;i<matrix.length;i++){
if(matrix[i][0] == target){
return true;
}
}
return false;
}
//如果是个正常的矩阵
if(matrix[0][0] > target){
//矩阵最小值都比目标值大
return false;
}
int pos = 0;
for(;pos < (matrix.length < matrix[0].length ? matrix.length : matrix[0].length);pos++){
if(matrix[pos][pos] == target){
//目标值在主对角线上
return true;
}
if(matrix[pos][pos] > target){
//遇到了第一个比目标值大的元素,便可以精确确定一个需要查找的范围(具体地说是两个小范围)
//查找第一个小范围
for(int i=0;i<pos;i++){
for(int j=pos;j<matrix[0].length;j++){
if(matrix[i][j] == target){
return true;
}
}
}
//查找第二个小范围
for(int i=pos;i<matrix.length;i++){
for(int j=0;j<pos;j++){
if(matrix[i][j] == target){
return true;
}
}
}
return false;
}
}
return false;
}
}