题目描述
- 在一个二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。
- 地址:牛客链接
解决方法
- 方法1:遍历整个二维数组,O(M*N)的时间复杂度,显然是不得分的答案
- 方法2:注意题干,每一行与每一列都是排好序的,看到排序与查找这两个字眼,自然想到二分查找,可以外层循环遍历行,然后对每一行进行二分查找,也可以外层循环遍历列,然后对每一列进行二分查找,时间复杂度都是 O(M*logN)的形式,那么为了更优化一点,可以让M等于 min(行数,列数)
方法3: 利用行列都排好序的特点,从右上角开始,用 row 与 col 标志当前数所在的行数与列数:
- 如果当前数字比 target大,说明当前列都不符合标准,那么向左移动,col–;
- 如果当前数字比 target 小,说明当前行都不符合标准,那么向下移动, row++;
- 如果两者相等,那么直接 return true
上述策略也有种缩小问题规模的思想,每一次淘汰一行或者一列,然后对新“形成”的二维数组进行迭代查找,row 与 col 始终标志新“形成”的二维数组的右上角元素。
但是要注意数组越界问题,所以当数组发生越界时,直接结束循环,返回 false
该方法时间复杂度为 O(M+N)。当然,也可以从左下角元素向右或者向上查找,同样的思想。
方法2与3根据数据规模而定,哪一个更优
经验教训
- 看到排序与查找这两个字眼,自然想到二分查找
- 如何一步步缩小问题规模(方法3),进而迭代或者递归。
代码实现
- 方法2:
public boolean Find(int target, int [][] array) {
//二维数组基本判断
if (array == null || array.length == 0 || array[0] == null || array[0].length == 0) {
return false;
}
int m = 0;
int n = 0;
//以min(m,n)作为外循环次数,时间复杂度为O(m*longn)
if (array.length < array[0].length) {
m = array.length;
n = array[0].length;
for (int row = 0; row < m; row++) {
int left = 0;
int right = n - 1;
while ( left <= right) {
int mid = ((left + right) >> 1);
if (array[row][mid] > target) {
right = mid - 1;
}else if (array[row][mid] < target) {
left = mid + 1;
}else {
return true;
}
}
}
}else {
m = array[0].length;
n = array.length;
for (int col = 0; col < m; col++) {
int left = 0;
int right = n - 1;
while ( left <= right) {
int mid = ((left + right) >> 1);
if (array[mid][col] > target) {
right = mid - 1;
}else if (array[mid][col] < target) {
left = mid + 1;
}else {
return true;
}
}
}
}
return false;
}
- 方法3:
public boolean Find(int target, int [][] array) {
//二维数组基本判断
if (array == null || array.length == 0 || array[0] == null || array[0].length == 0) {
return false;
}
//从右上角开始查找
int row = 0;
int col = array[0].length - 1;
while (row < array.length && col >= 0) {
//当前元素大于target,向左移动
if (array[row][col] > target) {
col--;
}else if (array[row][col] < target) {
//当前元素大于target,向下移动
row++;
}else {
//找到target
return true;
}
}
//未找到
return false;
}