文章目录
- 题目描述
- 知识点
- 解法一——二分
- 结果
- 码前思考
- 代码实现
- 解法二——思维⭐⭐⭐⭐⭐(重要!!!)
- 结果
- 码前思考
- 代码实现
- 码后反思
- 参考文档
- 二刷代码
矩阵的每行从左到右是升序,每列从上到下也是升序,在矩阵中查找某个数。
二分、分支、思维
看到有序,第一反应就是二分查找。最直接的做法,一行一行的进行二分查找即可。
此外,结合有序的性质,一些情况可以提前结束:
比如某一行的第一个元素大于了 target ,当前行和后边的所有行都不用考虑了,直接返回 false。
某一行的最后一个元素小于了 target ,当前行就不用考虑了,换下一行。
时间复杂度的话,如果是 m
行 n
列,就是 O ( m l o g ( n ) ) O(mlog(n)) O(mlog(n))。
class Solution {
public boolean searchMatrix(int[][] matrix, int target) {
if(matrix.length == 0 || matrix[0].length == 0){
return false;
}
for(int i=0;i<matrix.length;i++){
if(matrix[i][0] > target){
break;
}
if(matrix[i][matrix[i].length-1] < target){
continue;
}
int col = binarySearch(matrix[i],target);
if(col != -1){
return true;
}
}
return false;
}
private int binarySearch(int[] nums,int target){
int start = 0;
int end = nums.length -1;
while(start <= end){
int mid = (start+end) >>> 1;
if(nums[mid] == target){
return mid;
}else if(nums[mid] < target){
start = mid+1;
}else{
end = mid-1;
}
}
return -1;
}
}
需要很敏锐的观察力了。
数组从左到右和从上到下都是升序的,如果从右上角出发开始遍历呢?
会发现每次都是向左数字会变小,向下数字会变大,有点和二分查找树相似。二分查找树的话,是向左数字变小,向右数字变大。
所以我们可以把 target
和当前值比较。
target
的值大于当前值,那么就向下走。target
的值小于当前值,那么就向左走。true
。也可以换个角度思考。
如果 target
的值小于当前值,也就意味着当前值所在的列肯定不会存在 target
了,可以把当前列去掉,从新的右上角的值开始遍历。
同理,如果 target
的值大于当前值,也就意味着当前值所在的行肯定不会存在 target
了,可以把当前行去掉,从新的右上角的值开始遍历。
看下边的例子。
[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 = 9,如果我们从 15 开始遍历, cur = 15
target < 15, 去掉当前列, cur = 11
[1, 4, 7, 11],
[2, 5, 8, 12],
[3, 6, 9, 16],
[10, 13, 14, 17],
[18, 21, 23, 26]
target < 11, 去掉当前列, cur = 7
[1, 4, 7],
[2, 5, 8],
[3, 6, 9],
[10, 13, 14],
[18, 21, 23]
target > 7, 去掉当前行, cur = 8
[2, 5, 8],
[3, 6, 9],
[10, 13, 14],
[18, 21, 23]
target > 8, 去掉当前行, cur = 9, 遍历结束
[3, 6, 9],
[10, 13, 14],
[18, 21, 23]
不管从哪种角度考虑,代码的话都是一样的。
时间复杂度就是每个节点最多遍历一遍了, O ( m + n ) O(m + n) O(m+n)。
//从右上角开始遍历,类似于BST树来操作
class Solution {
public:
bool searchMatrix(vector<vector<int>>& matrix, int target) {
if(matrix.size() == 0 || matrix[0].size() == 0){
return false;
}
int row = 0;
int col = matrix[0].size()-1;
while(row < matrix.size() && col >= 0){
if(matrix[row][col] > target){
col--;
}else if(matrix[row][col] < target){
row++;
}else{
return true;
}
}
return false;
}
};
//采用DFS并且搭配上一定的剪枝操作
class Solution {
public:
vector<vector<bool> > vis;
bool flag = false; //用于标记是否找到
int dir[2][2] = {{1,0},{0,1}};
int m,n;
bool searchMatrix(vector<vector<int>>& matrix, int target) {
m = matrix.size();
if(m == 0){
return false;
}
n = matrix[0].size();
if(n == 0){
return false;
}
vis.assign(m,vector<bool>(n,false));
search(matrix,target,0,0);
return flag;
}
void search(vector<vector<int>>& matrix, int target,int x,int y){
//printf("x:%d y:%d\n",x,y);
if(!flag){
vis[x][y] = true;
if(matrix[x][y] == target){
flag = true;
return;
}else{
for(int i=0;i<2;i++){
int newX = x + dir[i][0];
int newY = y + dir[i][1];
if(newX >=m || newY >= n || vis[newX][newY] || flag){//被访问过了
continue;
}else{
//代表可以访问
search(matrix,target,newX,newY);
}
}
}
return;
}
}
};
//采用类似于二叉树的写法进行解题
class Solution {
public:
int dir[2][2] = {{0,-1},{1,0}};
bool searchMatrix(vector<vector<int>>& matrix, int target) {
bool isFind = false;
int m = matrix.size();
if(m==0){
return false;
}
int n = matrix[0].size();
int x=0;
int y=n-1;
//遇到边界就会挂掉
while(isFind != true && x<m && y>=0){
if(matrix[x][y] == target){
isFind=true;
}else{
if(target<matrix[x][y]){
x += dir[0][0];
y += dir[0][1];
}else{
x += dir[1][0];
y += dir[1][1];
}
}
}
return isFind;
}
};