力扣解题-74. 搜索二维矩阵

力扣解题-74. 搜索二维矩阵

给你一个满足下述两条属性的 m x n 整数矩阵:

  1. 每行中的整数从左到右按非严格递增顺序排列。
  2. 每行的第一个整数大于前一行的最后一个整数。

给你一个整数 target ,如果 target 在矩阵中,返回 true ;否则,返回 false 。

示例 1:

输入:matrix = [[1,3,5,7],[10,11,16,20],[23,30,34,60]], target = 3

输出:true

示例 2:

输入:matrix = [[1,3,5,7],[10,11,16,20],[23,30,34,60]], target = 13

输出:false

提示:

m == matrix.length

n == matrix[i].length

1 <= m, n <= 100

-10⁴ <= matrix[i][j], target <= 10⁴

Related Topics

数组、二分查找、矩阵


第一次解答

解题思路

核心方法:双层定位法(行二分+行内遍历),先通过二分查找定位target可能所在的行,再遍历该行元素验证是否存在target,时间复杂度O(logm + n)(m为行数,n为列数)、空间复杂度O(1),是贴合题目矩阵特性的直观解法。

核心逻辑拆解

该矩阵的核心特性是"整体有序"(每行递增且下一行首元素>上一行尾元素),因此可分两步定位target:

  1. 行定位:通过二分查找确定target可能存在的行(只有一行满足"行首≤target≤行尾");
  2. 行内验证:遍历该行所有元素,检查是否存在target,存在则返回true,否则返回false。
具体执行逻辑
  1. 初始化变量
    • left/right:行二分的左右边界,初始为0和matrix.length-1
    • n:矩阵列数,提前获取避免重复计算;
  2. 行二分循环left ≤ right):
    • 计算中间行索引mid = left + (right-left)/2
    • 获取中间行的首元素midBeginVal = matrix[mid][0]和尾元素midEndVal = matrix[mid][n-1]
    • 分三种情况调整边界:
      • target < midBeginVal:target在更上面的行,更新right = mid - 1
      • target > midEndVal:target在更下面的行,更新left = mid + 1
      • midBeginVal ≤ target ≤ midEndVal:找到目标行,遍历该行所有元素,找到target则返回true,否则返回false;
  3. 循环终止:若未找到目标行(left>right),返回false。
执行流程可视化(以示例1 matrix=[[1,3,5,7],[10,11,16,20],[23,30,34,60]]、target=3为例)
循环次数 left right mid midBeginVal midEndVal 比较结果 操作 结果
1 0 2 1 10 20 3<10 right=0 缩小行范围
2 0 0 0 1 7 1≤3≤7 遍历第0行[1,3,5,7] 找到3,返回true
关键细节说明
  • mid计算优化 :使用left + (right-left)/2避免left+right的整数溢出;
  • 行定位的唯一性:矩阵特性保证"最多只有一行满足行首≤target≤行尾",因此找到该行后无需继续二分;
  • 遍历终止条件:行内遍历找到target后立即返回true,无需遍历剩余元素;
  • 边界处理
    • target小于所有行首元素:最终right<0,返回false;
    • target大于所有行尾元素:最终left>matrix.length-1,返回false。
性能说明
  • 时间复杂度:O(logm + n)(行二分O(logm) + 行内遍历O(n));
  • 空间复杂度:O(1)(仅使用常数级额外变量);
  • 优势:
    1. 逻辑直观,先定位行再查元素,符合"先粗后细"的查找思路;
    2. 充分利用矩阵"行有序且行间递增"的特性,比直接遍历整个矩阵(O(mn))效率更高;
    3. 代码简洁,易理解和实现。
java 复制代码
public boolean searchMatrix(int[][] matrix, int target) {
    int left=0;
    int right=matrix.length-1;
    int n=matrix[0].length;
    while (left<=right){
        int mid=left+(right-left)/2;
        int midBeginVal=matrix[mid][0];
        int midEndVal=matrix[mid][n-1];
        if(target<midBeginVal){
            right=mid-1;
        }
        if(target>midEndVal){
            left=mid+1;
        }
        if(target>=midBeginVal&&target<=midEndVal){
            for (int i = 0; i < n; i++) {
                if (matrix[mid][i] == target) {
                    return true;
                }
            }
            return false;
        }
    }
    return false;
}

示例解答

解题思路

解法1:二维转一维二分法(最优解,O(log(mn)))

核心方法:利用矩阵"整体有序"的特性,将二维坐标(i,j)映射为一维索引idx = i*n + j,直接对整个矩阵做一维二分查找,时间复杂度优化至O(log(mn)),是本题的最优解法。

代码实现
java 复制代码
public boolean searchMatrix(int[][] matrix, int target) {
    if (matrix == null || matrix.length == 0 || matrix[0].length == 0) {
        return false;
    }
    int m = matrix.length;
    int n = matrix[0].length;
    int left = 0;
    int right = m * n - 1; // 一维索引右边界
    
    while (left <= right) {
        int mid = left + (right - left) / 2;
        // 将一维索引转换为二维坐标
        int row = mid / n;
        int col = mid % n;
        int midVal = matrix[row][col];
        
        if (midVal == target) {
            return true;
        } else if (midVal < target) {
            left = mid + 1; // 目标在右侧
        } else {
            right = mid - 1; // 目标在左侧
        }
    }
    return false;
}
核心逻辑说明
  1. 坐标映射
    • 一维索引转二维:row = idx / n(行号=索引÷列数),col = idx % n(列号=索引%列数);
    • 例如matrix为3行4列,idx=5 → row=5/4=1,col=5%4=1 → 对应matrix[1][1]=11;
  2. 一维二分 :将整个矩阵视为长度为m*n的有序数组,按标准二分查找逻辑执行:
    • 找到midVal等于target,返回true;
    • midVal<target,目标在右半区,left=mid+1;
    • midVal>target,目标在左半区,right=mid-1;
  3. 循环终止:left>right时,说明target不存在,返回false。
性能说明
  • 时间复杂度:O(log(mn))(仅需一次二分,比原解法的O(logm + n)更优,尤其当n较大时);
  • 空间复杂度:O(1)(与原解法一致);
  • 优势:
    1. 时间复杂度最优,充分利用矩阵"整体有序"的核心特性;
    2. 代码简洁,无需分两次查找,逻辑更闭环;
  • 劣势:需理解二维坐标与一维索引的映射关系,新手入门门槛略高。
解法2:行二分+行内二分法(进阶优化,O(logm + logn))

核心方法:先通过二分找到目标行,再对目标行做二分查找,时间复杂度O(logm + logn),结合了原解法的"行定位"和二分查找的高效性。

代码实现
java 复制代码
public boolean searchMatrix(int[][] matrix, int target) {
    int m = matrix.length;
    int n = matrix[0].length;
    int leftRow = 0;
    int rightRow = m - 1;
    int targetRow = -1;
    
    // 第一步:二分查找目标行
    while (leftRow <= rightRow) {
        int midRow = leftRow + (rightRow - leftRow) / 2;
        int rowFirst = matrix[midRow][0];
        int rowLast = matrix[midRow][n - 1];
        
        if (target >= rowFirst && target <= rowLast) {
            targetRow = midRow;
            break;
        } else if (target < rowFirst) {
            rightRow = midRow - 1;
        } else {
            leftRow = midRow + 1;
        }
    }
    
    // 未找到目标行
    if (targetRow == -1) {
        return false;
    }
    
    // 第二步:对目标行做二分查找
    int leftCol = 0;
    int rightCol = n - 1;
    while (leftCol <= rightCol) {
        int midCol = leftCol + (rightCol - leftCol) / 2;
        int val = matrix[targetRow][midCol];
        if (val == target) {
            return true;
        } else if (val < target) {
            leftCol = midCol + 1;
        } else {
            rightCol = midCol - 1;
        }
    }
    
    return false;
}
核心逻辑说明
  1. 行二分 :与原解法一致,找到目标行后记录为targetRow
  2. 行内二分:对目标行的列做二分查找,而非线性遍历,将行内查找的时间复杂度从O(n)降至O(logn);
  3. 结果验证:行内二分找到target则返回true,否则返回false。
性能说明
  • 时间复杂度:O(logm + logn) = O(log(mn))(与解法1理论复杂度一致);
  • 空间复杂度:O(1);
  • 优势:
    1. 比原解法更高效,尤其当列数n较大时;
    2. 逻辑分步清晰,先找行再找列,易调试;
  • 劣势:代码量略多于解法1,需两次二分循环。

总结

  1. 双层定位法(第一次解答):O(logm + n)时间+O(1)空间,逻辑直观,新手易理解;
  2. 二维转一维二分法:O(log(mn))时间+O(1)空间,最优解,充分利用矩阵整体有序特性;
  3. 行二分+行内二分法:O(log(mn))时间+O(1)空间,分步清晰,效率与最优解持平;
  4. 关键技巧
    • 核心思想:矩阵"每行递增且下一行首>上一行尾" → 整体等价于一维有序数组,可直接用二分查找;
    • 坐标映射:二维转一维的核心是row=idx/n,col=idx%n,是解法1的关键;
    • 性能选择:小矩阵用原解法即可,大矩阵优先选解法1(一维二分)或解法2(行+列二分)。
相关推荐
xiaoye-duck2 小时前
《算法题讲解指南:优选算法-分治-快排》--45.数组中的第k个最大元素,46.最小的k个数
c++·算法
Galerkin码农选手2 小时前
per_tenor_quant_fp8和per_token_quant_fp8算法解读
人工智能·pytorch·算法
tankeven2 小时前
HJ125 最大最小路
c++·算法
MegaDataFlowers3 小时前
认识复杂度和简单排序算法
java·算法·排序算法
MSTcheng.3 小时前
【算法】前缀和:『560. 和为 K 的子数组 & 1314.矩阵区域和』
线性代数·算法·矩阵
luckycoding3 小时前
739. 每日温度
算法·leetcode·职场和发展
一只黑鸟3 小时前
基于STM32的罐装水泥成分实时检测系统设计与实现(含有matlab仿真)
stm32·嵌入式硬件·算法·matlab·毕设
@我漫长的孤独流浪3 小时前
C算法设计与分析------程序设计代码
数据结构·c++·算法
Filotimo_3 小时前
3.5 排序算法
数据结构·算法·排序算法