1. 题目
原题链接
编写一个高效的算法来判断 m x n 矩阵中,是否存在一个目标值。该矩阵具有如下特性:
每行中的整数从左到右按升序排列。
每行的第一个整数大于前一行的最后一个整数。
示例 1:
输入:matrix = [[1,3,5,7],[10,11,16,20],[23,30,34,60]], target = 3
输出:true
示例 2:
输入:matrix = [[1,3,5,7],[10,11,16,20],[23,30,34,60]], target = 13
输出:false
提示:
m == matrix.length
n == matrix[i].length
1 <= m, n <= 100
-104 <= matrix[i][j], target <= 104
Related Topics 数组 二分查找
👍 446 👎 0
2. 题解
2.1 解法1: 从右上角开始搜索(与 240. 搜索二维矩阵 II 完全相同)
设坐标 x, y , 初始时为右上角,
当 坐标值 > target , y–
当 坐标值 < target, x++
当 坐标值 = target, 返回 true
class Solution {
public boolean searchMatrix(int[][] matrix, int target) {
int m = matrix.length;
int n = matrix[0].length;
int row = 0;
int col = n - 1;
while (row < m && col >= 0) {
if (matrix[row][col] == target) {
return true;
} else if (matrix[row][col] < target) {
row++;
} else {
col--;
}
}
return false;
}
}
2.2 解法2: 两次二分
由于行列都有序, 第一次查找目标行, 第二次在目标行查找相应的列
第一次二分: 从上往下找, 找到最后一个 matrix<=target 的行
第二次二分: 从所在行中定位到列(从左到右,找到最后一个满足 matrix[row][x] <= target
的列号) 或者可以使用标准的二分查找代码
class Solution {
public boolean searchMatrix(int[][] matrix, int target) {
if (matrix[0][0] > target) {
return false;
}
int m = matrix.length, n = matrix[0].length;
int left = 0, right = m - 1;
while (left < right) { // 找到最后一个 matrix<=target 的行
int mid = right + left + 1 >> 1;
if (matrix[mid][0] > target) {
right = mid - 1;
} else {
left = mid;
}
}
int row = left;
while (left <= right) {// 简单二分找元素是否存在
int mid = right + left >> 1;
if (matrix[row][mid] == target) {
return true;
} else if (matrix[row][mid] < target) {
left = mid + 1;
} else {
right = mid - 1;
}
}
return false;
}
}
注意点:
开始写的时候有两种写法, 我写的第一种方式如下, 最后实际落在 第一个 >target 的元素上
int left = 0, right = m - 1;
while (left < right) {
int mid = right + left >> 1;
if (matrix[mid][0] > target) {
right = mid;
} else {
left = mid + 1;
}
}
第二种方式, 会直接落在 最后一个 <=target 的元素上
int left = 0, right = m - 1;
while (left < right) {
int mid = right + left + 1 >> 1;
if (matrix[mid][0] > target) {
right = mid - 1;
} else {
left = mid;
}
}
注意其中细小的差别, 1.mid 的计算 2.下一次 left 与 right下标的变化
根据题意选择第二种, 如果用第一种, 那么所需的下标为 left-1
, 这时要多考虑 只有一个元素的情况, 比如可能 left=0
总结: 两种写法都可以, 我个人一般用第一种比较多, 每次避免出错的办法就是 在草稿纸上推算一下, 一个元素及两个元素时的情况, 将所有情况都考虑到
2.3 解法3: 一次二分
主要思想与解法 1 类似, 因为将二维矩阵的行尾和行首连接,也具有单调性, 将二维数组通过下标转换 看成一维数组 进行二分查找即可
这里二维数组下标与 一维下标的关系如下(设n为数组的列数):
temp[mid]即为 matrix[mid/n][mid%n]
class Solution {
public boolean searchMatrix(int[][] matrix, int target) {
int m = matrix.length;
int n = matrix[0].length;
int left = 0;
int right = m * n - 1;
while (left <= right) {
int mid = left + right >> 1;
if (matrix[mid / n][mid % n] == target) {
return true;
} else if (matrix[mid / n][mid % n] < target) {
left = mid + 1;
} else {
right = mid - 1;
}
}
return false;
}
}