算法题解记录18+++搜索二维矩阵Ⅱ

发布于:2024-04-23 ⋅ 阅读:(23) ⋅ 点赞:(0)

        本题可以说是运用二分查找的典例,即使是面对矩阵,只要是它保持“排序好”这样的结构,就一定能采用二分查找法。【你知道的,对于排序好的数组,二分查找几乎是最优秀的算法】

        当然,答案提供的是“Z字形查找法”【这个概念我搜不到,不过其实质,就是矩阵边界的缩减】

        “Z字形”排查效率高于二分查找法,只是因为题目结构特殊,一般不会有这种结构。

题目描述:

        编写一个高效的算法来搜索 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
  • -10^9 <= matrix[i][j] <= 10^9
  • 每行的所有元素从左到右升序排列
  • 每列的所有元素从上到下升序排列
  • -10^9 <= target <= 10^9

解题准备:

        1.理解题意:本题的结构,非常特殊,对于matrix中的任一元素【i,j】,其具备四个属性,上、左的元素小于它,下、右的元素大于它【对于边界元素不讨论,类似】

        2.理解可能具备的操作:就此,大概率只需要查找一种操作即可。

        3.朴素的思路:对于矩阵,最简单的遍历原则就是,暴力遍历【从0行到len-1行,从0列到len2-1列,依次遍历元素】

        代码为:

for(int i=0; i<m; i++){
    for(int j=0; j<n; j++){
        // m是行数,n是列数。
        // 在这个语句段里操作
    }
}

        不过,虽然这样做,一定能找到target(或者发现根本没有),却浪费了约束条件:每一行、列存在递增的关系。

解题难点1分析:

        如果不从二分查找的角度,对于这种结构比较容易想到的优化方法为:

        1.遍历第0行的元素,依次判断大小(如果< target,+1),如果等于target直接返回true,如果大于target说明这一行不可能存在target,遍历下一行。

        2.接着上述,遍历第1行,同理……

        问题:时间复杂度仍高

                对于这种优化方案,如果是下图的结构:

                容易发现,其遍历次数与暴力for一样多,所以时间复杂度的优化很差。

                就算是一般的结构,如果target比较大,很大概率前面k行,也得全部遍历(k随机),也就是说,时间复杂度没怎么优化。从最差优化的角度分析,时间复杂度仍是O(m*n);

        没办法,这种优化不可取,得从别的角度出发。

解题难点2分析:

        从二分查找的角度出发,既然行列间满足递增的约束,那么对于每一行,必然也满足递增的约束。

        拆分每一行,拆成0到len-1行,每一行用二分查找查找,时间复杂度为O(logN)。

        由于遍历0到len-1行,所以总的时间复杂度是O(m * log n);

        代码在此不写了,比较简单。

        不过,这道题是个强约束,拆分后,反而增加了查找时间复杂度。

解题难点3分析:

        从结构本身出发,由于行列间递增,那么,应该能处理特殊的数据。

        什么意思呢?我们用【0,0】,【0,len-1】,【len2-1,0】,【len-1,len2-1】维护矩阵的四个角的数据。

        由于矩阵的特殊性质,我们知道:【0,0】一定是最小的,【0,len-1】一定是0行最大的,【len2-1,0】一定是0列最大的,【len-1, len2-1】一定是所有数据最大的。

        如果矩阵中存在target,那么它一定大于【0,0】,小于【len-1,len2-1】。

        如果target大于【0, len-1】呢?这说明,target大于0行所有元素,那需要继续查找0行吗?

        可以直接忽略,从第1行开始查找。

        不过,target可能小于【0, len-1】,同时大于【0, 0】,这说明它可能在第0行。

        这时候从【len2-1, 0】开始判断,问题又来了:可能target同时大于【0,0】,小于【len2-1,0】

        不过,如果target小于【len2-1, 0】,不就说明:第len2-1行,都比target大吗?(因为【len2-1,0】在该行最小)

        那么,往上移动一行,不就行了?        

        其实从这个角度,看【0, len-1】也一样,如果target小于【0,len-1】,不就说明target小于len-1列的所有元素吗?

        往回退一列,不就行了?

        为了保持一致性,建议在四个角中,拿一个元素操作即可。

        该算法,每次可以排除一行或者一列,效率更高一些。

以上内容即我想分享的关于力扣热题18的一些知识。

        我是蚊子码农,如有补充,欢迎在评论区留言。个人也是初学者,知识体系可能没有那么完善,希望各位多多指正,谢谢大家。