剑指 Offer 04. 二维数组中的查找【数组 / BST】

10 篇文章 0 订阅
9 篇文章 0 订阅

题目描述

在这里插入图片描述

矩阵的每行从左到右是升序,每列从上到下也是升序,在矩阵中查找某个数。

知识点

二分、分支、思维

解法一——二分

结果

在这里插入图片描述

码前思考

看到有序,第一反应就是二分查找。最直接的做法,一行一行的进行二分查找即可

此外,结合有序的性质,一些情况可以提前结束:

  1. 比如某一行的第一个元素大于了 target ,当前行和后边的所有行都不用考虑了,直接返回 false。

  2. 某一行的最后一个元素小于了 target ,当前行就不用考虑了,换下一行。

时间复杂度的话,如果是 mn 列,就是 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)

代码实现

class Solution {
public:
    bool findNumberIn2DArray(vector<vector<int>>& matrix, int target) {
        int i = matrix.size() - 1, j = 0;
        while(i >= 0 && j < matrix[0].size())
        {
            if(matrix[i][j] > target) i--;
            else if(matrix[i][j] < target) j++;
            else return true;
        }
        return false;
    }
};

码后反思

  1. 之前使用的是带递归的BST查找,没有使用循环,从而由于递归导致时间复杂度和空间复杂度都不理想。

    //使用类似于二分查找树的方式进行搜索
    class Solution {
    public:
        bool findNumberIn2DArray(vector<vector<int>>& matrix, int target) {
            int n = matrix.size();
            if(n==0){
                return false;
            }
            int m = matrix[0].size();
            bool res = bstSearch(0,m-1,target,matrix,n,m);
    
            return res;
        }
    
        bool bstSearch(int x,int y,int target,vector<vector<int>>& matrix,int n,int m){
            if(x>=n || x<0){
                return false;
            }else if(y>=m || y<0){
                return false;
            }else{
                //printf("x:%d y:%d val:%d\n",x,y,matrix[x][y]);
                if(matrix[x][y] == target){
                    return true;
                }else if(matrix[x][y] > target){
                    return bstSearch(x,y-1,target,matrix,n,m);
                }else{
                    return bstSearch(x+1,y,target,matrix,n,m); 
                }
            }
            return true;
        }
    };
    
  2. 这应该是第3次做这道题目了。

参考文档

  1. 详细通俗的思路分析,多解法
  2. 面试题04. 二维数组中的查找(标志数,清晰图解)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值