给你一个满足下述两条属性的 m x n
整数矩阵:
- 每行中的整数从左到右按非严格递增顺序排列。
- 每行的第一个整数大于前一行的最后一个整数。
给你一个整数 target
,如果 target
在矩阵中,返回 true
;否则,返回 false
。
示例 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
-10^4 <= matrix[i][j], target <= 10^4
解法1:两次二分查找
由于每行的第一个元素大于前一行的最后一个元素,且每行元素是升序的,所以每行的第一个元素大于前一行的第一个元素,因此矩阵第一列的元素是升序的。
我们可以对矩阵每行第一列的元素二分查找,找到最后一个不大于目标值的元素,然后在该元素所在行中二分查找目标值是否存在。
Java版:
class Solution {
public boolean searchMatrix(int[][] matrix, int target) {
int m = matrix.length;
int n = matrix[0].length;
int l = 0;
int r = m - 1;
while (l <= r) {
// 循环不变量:
// matrix[l - 1][0] < target
// matrix[r + 1][0] > target
int mid = l + (r - l) / 2;
if (matrix[mid][0] == target) {
return true;
}
if (matrix[mid][0] < target) {
l = mid + 1;
} else {
r = mid - 1;
}
}
// l == r + 1
// 此时 matrix[l][0] > target, matrix[r][0] < target
int row = r;
if (row < 0 || row >= m) {
return false;
}
l = 0;
r = n - 1;
while (l <= r) {
int mid = l + (r - l) / 2;
if (matrix[row][mid] == target) {
return true;
}
if (matrix[row][mid] < target) {
l = mid + 1;
} else {
r = mid - 1;
}
}
return false;
}
}
Python版:
class Solution:
def searchMatrix(self, matrix: List[List[int]], target: int) -> bool:
m = len(matrix)
n = len(matrix[0])
l = 0
r = m - 1
while l <= r:
# 循环不变量:
# matrix[l - 1][0] < target
# matrix[r + 1][0] > target
mid = l + (r - l) // 2
if matrix[mid][0] == target:
return True
if matrix[mid][0] < target:
l = mid + 1
else:
r = mid - 1
# l == r + 1
row = r
if row < 0 or row >= m:
return False
l = 0
r = n - 1
while l <= r:
mid = l + (r - l) // 2
if matrix[row][mid] == target:
return True
if matrix[row][mid] < target:
l = mid + 1
else:
r = mid - 1
return False
复杂度分析
-
时间复杂度:O(log m + log n) = O(log mn),其中 m 是矩阵的行数, n 是矩阵的列数。
-
空间复杂度:O(1)。
解法2:一次二分查找
若将矩阵每一行拼接在上一行的末尾,则会得到一个升序数组,我们可以在该数组上二分找到目标元素。代码实现时,可以二分升序数组的下标,将其映射到原矩阵的行和列上。
Java版:
class Solution {
public boolean searchMatrix(int[][] matrix, int target) {
int m = matrix.length;
int n = matrix[0].length;
int l = 0;
int r = m * n - 1;
while (l <= r) {
// 循环不变量:
// matrix[(l - 1) / n][(l - 1) % n] < target
// matrix[(r + 1) / n][(r + 1) % n] > target
int mid = l + (r - l) / 2;
int x = matrix[mid / n][mid % n];
if (x == target) {
return true;
}
if (x < target) {
l = mid + 1;
} else {
r = mid - 1;
}
}
return false;
}
}
Python3版:
class Solution:
def searchMatrix(self, matrix: List[List[int]], target: int) -> bool:
m, n = len(matrix), len(matrix[0])
l, r = 0, m * n - 1
while l <= r:
mid = l + (r - l) // 2
x = matrix[mid // n][mid % n]
if x == target:
return True
if x < target:
l = mid + 1
else:
r = mid - 1
return False
复杂度分析
-
时间复杂度:O(log m + log n) = O(log mn),其中 m 是矩阵的行数, n 是矩阵的列数。
-
空间复杂度:O(1)。
结语
两种方法殊途同归,都利用了二分查找,在二维矩阵上寻找目标值。值得注意的是,若二维数组中的一维数组的元素个数不一,方法二将会失效,而方法一则能正确处理。