力扣 搜索二维矩阵

二分查找,闭区间与开区间的不同解法。

题目

乍一看,不是遍历一下找到元素就可以了。

java 复制代码
class Solution {
    public boolean searchMatrix(int[][] matrix, int target) {

        for (int[] ints : matrix) {
            for (int ans : ints) {
                if (ans == target) return true;
            }
        }
        
        return false;

    }
}

可以通过的,但还是可以优化一下的。这题可以用二分的思路,可以把整个矩阵看成一大个数组,若将矩阵每一行拼接在上一行的末尾,则会得到一个升序数组,我们可以在该数组上二分找到目标元素。但是这种方法在每一行元素个数不一时会失效。

时间复杂度:O(logmn),空间复杂度:O(1)。

java 复制代码
class Solution {
    public boolean searchMatrix(int[][] matrix, int target) {
        int m = matrix.length, n = matrix[0].length;
        int low = 0, high = m * n - 1;
        while (low <= high) {
            int mid = (high - low) / 2 + low;
            int x = matrix[mid / n][mid % n];
            if (x < target) {
                low = mid + 1;
            } else if (x > target) {
                high = mid - 1;
            } else {
                return true;
            }
        }
        return false;
    }
}

以上为标准二分查找的写法,其中首尾做为边界,条件时while (low <= high),接着如果目标值偏大则在右边找,即low = mid + 1,反之,目标值偏小时往左边找,即 high = mid - 1,然后当low指针从左边到target时扫了一遍,当high指针从右边到target时扫了一遍,当两个指针重叠相遇时,进一步判断两个指针定住的数是不是目标数,然后退出循环。

这题还可以先对行二分,再对列二分,进行两次二分查找。每行的第一个元素大于前一行的第一个元素,矩阵第一列的元素是升序的。对矩阵的第一列的元素二分查找,找到最后一个不大于目标值的元素,然后在该元素所在行中二分查找目标值是否存在。

时间复杂度:O(logmn),空间复杂度:O(1)。

java 复制代码
class Solution {
    public boolean searchMatrix(int[][] matrix, int target) {
        int rowIndex = binarySearchFirstColumn(matrix, target);
        if (rowIndex < 0) {
            return false;
        }
        return binarySearchRow(matrix[rowIndex], target);
    }

    public int binarySearchFirstColumn(int[][] matrix, int target) {
        int low = -1, high = matrix.length - 1;
        while (low < high) {
            int mid = (high - low + 1) / 2 + low;
            if (matrix[mid][0] <= target) {
                low = mid;
            } else {
                high = mid - 1;
            }
        }
        return low;
    }

    public boolean binarySearchRow(int[] row, int target) {
        int low = 0, high = row.length - 1;
        while (low <= high) {
            int mid = (high - low) / 2 + low;
            if (row[mid] == target) {
                return true;
            } else if (row[mid] > target) {
                high = mid - 1;
            } else {
                low = mid + 1;
            }
        }
        return false;
    }
}

其中,用了一个左开右闭区间的二分查找,可以先进行扩充边界,然后条件改为while (low < high),接着low要设置为mid,退出的条件即当low跟high重叠时,此时的low是mid了,看是不是要找的target。当然,这题用左闭右开区间的二分查找也是类似,不过要注意以下返回值,要取到不大于目标值的最大行索引。

java 复制代码
class Solution {
    public boolean searchMatrix(int[][] matrix, int target) {
        int rowIndex = binarySearchFirstColumn(matrix, target);
        if (rowIndex < 0) {
            return false;
        }
        return binarySearchRow(matrix[rowIndex], target);
    }
public int binarySearchFirstColumn(int[][] matrix, int target) {
    int low = 0, high = matrix.length;  
    while (low < high) {  
        int mid = (high - low) / 2 + low;  
        if (matrix[mid][0] <= target) {  
            low = mid + 1;
        } else {  
            high = mid;  
        }
    }
    return high-1; 
}
    public boolean binarySearchRow(int[] row, int target) {
        int low = 0, high = row.length - 1;
        while (low <= high) {
            int mid = (high - low) / 2 + low;
            if (row[mid] == target) {
                return true;
            } else if (row[mid] > target) {
                high = mid - 1;
            } else {
                low = mid + 1;
            }
        }
        return false;
    }
}

然后,也可以用最经典的标准二分模板去写。

java 复制代码
class Solution {
    public boolean searchMatrix(int[][] matrix, int target) {
        int rowIndex = binarySearchFirstColumn(matrix, target);
        if (rowIndex < 0) {
            return false;
        }
        return binarySearchRow(matrix[rowIndex], target);
    }
public int binarySearchFirstColumn(int[][] matrix, int target) {
    // 初始化low为0,high为矩阵最后一行的索引
    int low = 0, high = matrix.length - 1;
    while (low <= high) {  // 当low不大于high时继续循环
        int mid = (high - low) / 2 + low;  // 计算中间位置
        // 如果中间位置的行的第一个元素小于或等于目标值,则该行及之后的行可能是候选行
        if (matrix[mid][0] <= target) {
            low = mid + 1;  
        } else {
            high = mid - 1; 
        }
    }
    // 返回不大于目标值的最大行索引
    return high;  // 这里返回high,因为high指向的是不大于目标值的最大行索引或未找到返回-1
    
  
}
    public boolean binarySearchRow(int[] row, int target) {
        int low = 0, high = row.length - 1;
        while (low <= high) {
            int mid = (high - low) / 2 + low;
            if (row[mid] == target) {
                return true;
            } else if (row[mid] > target) {
                high = mid - 1;
            } else {
                low = mid + 1;
            }
        }
        return false;
    }
}

二分查找模板巧记,先写好数组中的左右边界值,然后while (low <= high),接着写mid的求值 int mid = (high - low) / 2 + low,再到判断,若目标值偏大往大的找即low指针右移,若目标值偏小往小的找即high指针左移,最后当low跟high重叠时对当前元素做判断,返回值依题而定。

相关推荐
越来越无动于衷1 分钟前
代理模式深度解析:从静态代理到 Spring AOP 实现
java·spring·代理模式
Meteors.2 分钟前
23种设计模式——适配器模式(Adapter)详解
java·设计模式·适配器模式
喂完待续13 分钟前
【序列晋升】12 Spring Boot 约定优于配置
java·spring boot·spring·架构·约定大于配置·序列晋升·tech arch
PAK向日葵40 分钟前
【算法导论】MT 0823笔试题题解
算法·面试
智驱力人工智能1 小时前
安全帽检测算法如何提升工地安全管理效率
人工智能·算法·安全·边缘计算·安全帽检测·口罩检测·安全鞋检测
David爱编程1 小时前
Java 守护线程 vs 用户线程:一文彻底讲透区别与应用
java·后端
艾莉丝努力练剑1 小时前
【C语言16天强化训练】从基础入门到进阶:Day 6
c语言·数据结构·学习·算法
即将进化成人机2 小时前
Maven架构的依赖管理和项目构建
java·架构·maven
qianmoq2 小时前
第03章:无限流:generate()和iterate()的神奇用法
java
快去睡觉~2 小时前
力扣1005:k次取反后最大化的数组和
数据结构·算法·leetcode