- 74. 搜索二维矩阵
编写一个高效的算法来判断 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
思路:
方法一:两次二分查找【通用,必会】
由于每行的第一个元素大于前一行的最后一个元素,且每行元素是升序的,所以每行的第一个元素大于前一行的第一个元素,因此矩阵第一列的元素是升序的。
我们可以对矩阵的第一列的元素二分查找,找到最后一个不大于目标值的元素,然后在该元素所在行中二分查找目标值是否存在。
// 法1:二分*2次(先第一列-获取行号row_i,后在row_i行继续二分查找target)
public boolean searchMatrix_v1(int[][] matrix, int target) {
int row_i = BinSearch1_col0(matrix, target);
// System.out.println(row_i);
if (row_i == -1) return false;
return BinSearch2_row(matrix[row_i], target);
}
// 找到最后一个不大于target的matrix第一列元素
// 二分1st - [left, mid-1], [mid, right]
private int BinSearch1_col0(int[][] matrix, int target) {
int start = -1, end = matrix.length - 1;
while (start < end) {
int mid = (start + 1) + end >> 1; //
if (matrix[mid][0] <= target)
start = mid;
else end = mid - 1;
}
return start;
}
// 二分2nd - [left, mid-1], mid, [mid+1, right]
private boolean BinSearch2_row(int[] row, int target) {
int start = 0, end = row.length - 1;
while (start <= end) {
int mid = start + end >> 1;
if (row[mid] == target)
return true;// mid;
else if (row[mid] < target)
start = mid+1;
else end = mid-1;
}
return false;
}
解法二【推荐】 那么,如何让方法高效呢?
根据题目我们知道,如果把a + 1行的头部连接到a行的末尾,那么我们可以得到一行递增的数值。也就是说,如果我们把矩阵中每一行都按照刚刚说的方式拼接在一起,那么一个矩阵就变成了递增数组。
我们对于这个递增数组进行二分搜素,就可以得到答案。下面是代码:
// 法2:二分*1次【推荐】
public boolean searchMatrix_v2_1(int[][] matrix, int target) {
int m = matrix.length, n = matrix[0].length;
int start = 0, end = m * n - 1;
while (start + 1 < end) {
int mid = start + end >> 1;
// row = mid / n, col = mid % n
int mid_val = matrix[mid / n][mid % n];
if (mid_val == target) return true;
else if (mid_val < target)
start = mid;
else end = mid;
}
if (matrix[start / n][start % n] == target) return true;
else if (matrix[end / n][end % n] == target) return true;
return false;
}
// 法2-2:(二分模板3)- [left, mid-1], mid, [mid+1, right]
public boolean searchMatrix_v2_2(int[][] matrix, int target) {
int m = matrix.length, n = matrix[0].length;
int start = 0, end = m * n - 1;
while (start <= end) { // [left, mid-1], mid, [mid+1, right]
int mid = start + end >> 1;
int mid_val = matrix[mid / n][mid % n]; // row = mid / n, col = mid % n
if (mid_val == target) return true;
else if (mid_val < target)
start = mid+1;
else end = mid-1;
}
return false;
}
解法三
我们能否根据特殊位置的特征来寻找元素?比如以右上角或左下角的元素为标准,开始搜索?
我们分析一下右上角的元素。
如果我们一开始拿右上角的元素做比较,我们的目标(target)如果大于右上角元素,那么target应该在右上角元素的下方;如果小于,那么在右上角元素的左边。我们通过控制行row和列column,可以以右上角元素为初始标度,寻找target是否存在。
// 法3:(BST搜索)
public boolean searchMatrix(int[][] matrix, int target) {
int row = 0, col = matrix[0].length - 1;
// 以右上角为起点root, 看作BST(查找方向: 向下row++/向左col--)
while (row < matrix.length && col >= 0) {
if (matrix[row][col] == target) return true;
else if (matrix[row][col] < target)
row++; // 如果目标值大,那么下一步往下找
else if (matrix[row][col] > target)
col--; // 如果目标值小,那么下一步往左找
}
return false;
}