题目描述
在一个二维数组中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。
解法一
最基础的解法,两重循环,暴力枚举。时间复杂度为O(m*n)
public class Solution {
public boolean Find(int target, int [][] array) {
if (array.length == 0 || array[0].length == 0)
return false;
for (int i = 0; i < array.length; i++)
for (int j = 0; j < array[i].length; j++)
if (array[i][j] == target)
return true;
return false;
}
}
解法二
枚举每一行,如果第一列小于或等于target
,最后一列大于或等于target
,则说明target
有可能存在于这一行,对其进行二分查找。时间复杂度为O(m*log(n))
public class Solution {
public boolean Find(int target, int [][] array) {
if (array.length == 0 || array[0].length == 0)
return false;
for (int i = 0; i < array.length; i++) {
if (array[i][0] <= target && array[i][array[i].length - 1] >= target) {
int left = 0, right = array[i].length - 1, mid;
while (left <= right) {
mid = left + (right - left) / 2;
if (array[i][mid] < target)
left = mid + 1;
else if (array[i][mid] > target)
right = mid - 1;
else
return true;
}
}
if (array[i][0] > target)
break;
}
return false;
}
}
解法三
二分查找行号,第一个最后一列大于target
的行,记为begin
;二分查找行号,最后一个第0列小于target
的行,记为end
;
从begin
到end
枚举每一行,对其进行二分查找。
时间复杂度为O(m*log(n))
public class Solution {
public boolean Find(int target, int [][] array) {
if (array.length == 0 || array[0].length == 0)
return false;
int i, begin = -1, end = -1;
int left = 0, right = array.length - 1, mid;
// 找到第0列最后一个小于target的数字
while (left <= right) {
mid = left + (right - left) / 2;
if (array[mid][0] < target) {
end = mid;
left = mid + 1;
} else if (array[mid][0] == target)
return true;
else
right = mid - 1;
}
// 找到最后一列第一个大于target的数字
left = 0;
right = array.length - 1;
while (left <= right) {
mid = left + (right - left) / 2;
if (array[mid][array[0].length - 1] < target)
left = mid + 1;
else if (array[mid][array[0].length - 1] == target)
return true;
else {
begin = mid;
right = mid - 1;
}
}
if (begin == -1 || end == -1)
return false;
// System.out.println(begin + " " + end);
for (i = begin; i <= end; i++) {
// 对每一行进行二分查找
left = 0;
right = array[i].length - 1;
while (left <= right) {
mid = left + (right - left) / 2;
if (array[i][mid] < target)
left = mid + 1;
else if (array[i][mid] == target)
return true;
else
right = mid - 1;
}
}
return false;
}
}
解法四
从左下角开始枚举,如果小于target
则col++
;如果大于target
则row--
。
时间复杂度为O(m+n)
public class Solution {
public boolean Find(int target, int [][] array) {
if (array.length == 0 || array[0].length == 0)
return false;
int row = array.length - 1, col = 0;
while (row >= 0 && col < array[0].length) {
if (array[row][col] < target)
col++;
else if (array[row][col] > target)
row--;
else
return true;
}
return false;
}
}
也可以从右上角进行枚举,如果小于target
则row++
;如果大于target
则col--
。
但是不能从左上角和右下角开始,因为左上角和右下角对于每种情况都存在两种方向的选择。