题目
编写一个高效的算法来搜索 m x n
矩阵 matrix
中的一个目标值 target
。该矩阵具有以下特性:
- 每行的元素从左到右升序排列。
- 每列的元素从上到下升序排列。
示例 1:
输入:matrix = [[1,4,7,11,15],[2,5,8,12,19],[3,6,9,16,22],[10,13,14,17,24],[18,21,23,26,30]], target = 5 输出:true
示例 2:
输入:matrix = [[1,4,7,11,15],[2,5,8,12,19],[3,6,9,16,22],[10,13,14,17,24],[18,21,23,26,30]], target = 20 输出:false
提示:
m == matrix.length
n == matrix[i].length
1 <= n, m <= 300
-109 <= matrix[i][j] <= 109
- 每行的所有元素从左到右升序排列
- 每列的所有元素从上到下升序排列
-109 <= target <= 109
题解
解法一 缩减数组
通过遍历最后一行和最后一列,找到第一个比target值大的那一行/列,从而缩减了数组
class Solution {
public boolean searchMatrix(int[][] matrix, int target) {
int m = matrix.length, n = matrix[0].length;
if (matrix[m - 1][n - 1] < target || matrix[0][0] > target) return false;
// 1. 遍历找到最后一个元素大于目标值的列 从这一列开始 缩减了目标矩阵
int begin_row = 0, begin_line = 0;
for (int i = 0; i < m; i++) {
if (matrix[i][n - 1] == target) {
return true;
} else if (matrix[i][n - 1] > target) {
begin_row = i;
break;
}
}
for (int i = 0; i < n; i++) {
if (matrix[m - 1][i] == target) {
return true;
} else if (matrix[m - 1][i] > target) {
begin_line = i;
break;
}
}
// ==== 找到了缩减后的矩阵,开始遍历
for (int i = begin_row; i < m; i++)
for (int j = begin_line; j < n; j++)
if (matrix[i][j] == target) return true;
return false;
}
}
解法二 二分查找
因为每一行每一列都是有序的,可以直接对每一行二分查找
class Solution {
public boolean searchMatrix(int[][] matrix, int target) {
for (int[] row : matrix) {
int index = search(row, target);
if (index >= 0) {
return true;
}
}
return false;
}
public int search(int[] nums, int target) {
int low = 0, high = nums.length - 1;
while (low <= high) {
int mid = (high - low) / 2 + low;
int num = nums[mid];
if (num == target) {
return mid;
} else if (num > target) {
high = mid - 1;
} else {
low = mid + 1;
}
}
return -1;
}
}
时间复杂度O(MlogN)
解法三 最优解法 Z字查找
以matrix的右上角为起点,这个点有这样的特征:
- 是该行中最大的元素
- 是该列中最小的元素
因此判断目标值与这个元素的大小
- 如果该点(右上角)大于target,说明不可能在这一列中(该点所在列都大于该点),把这一列直接去掉(让该点左移)
class Solution {
public boolean searchMatrix(int[][] matrix, int target) {
int ponit_row = 0, ponit_line = matrix[0].length - 1;
while (ponit_line >= 0 && ponit_row < matrix.length) {
if (matrix[ponit_row][ponit_line] > target) {
ponit_line--;
} else if (matrix[ponit_row][ponit_line] < target) {
ponit_row++;
} else {
return true;
}
}
return false;
}
}
时间复杂度:O(M+N)
小结
今天这道题的重点是找到起始点右上角点
这几天的矩阵类型的题目大致有这几类:
- 对矩阵的原地操作 : 这一类题的重点在于如何在不影响后续操作的前提上对行/列进行原地标记
- 在矩阵中的螺旋遍历
- 对矩阵进行空间翻转
- 对矩阵进行缩减