力扣解题-74. 搜索二维矩阵
给你一个满足下述两条属性的 m x n 整数矩阵:
- 每行中的整数从左到右按非严格递增顺序排列。
- 每行的第一个整数大于前一行的最后一个整数。
给你一个整数 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:
- 行定位:通过二分查找确定target可能存在的行(只有一行满足"行首≤target≤行尾");
- 行内验证:遍历该行所有元素,检查是否存在target,存在则返回true,否则返回false。
具体执行逻辑
- 初始化变量 :
left/right:行二分的左右边界,初始为0和matrix.length-1;n:矩阵列数,提前获取避免重复计算;
- 行二分循环 (
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;
- 若
- 计算中间行索引
- 循环终止:若未找到目标行(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)(仅使用常数级额外变量);
- 优势:
- 逻辑直观,先定位行再查元素,符合"先粗后细"的查找思路;
- 充分利用矩阵"行有序且行间递增"的特性,比直接遍历整个矩阵(O(mn))效率更高;
- 代码简洁,易理解和实现。
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;
}
核心逻辑说明
- 坐标映射 :
- 一维索引转二维:
row = idx / n(行号=索引÷列数),col = idx % n(列号=索引%列数); - 例如matrix为3行4列,idx=5 → row=5/4=1,col=5%4=1 → 对应matrix[1][1]=11;
- 一维索引转二维:
- 一维二分 :将整个矩阵视为长度为
m*n的有序数组,按标准二分查找逻辑执行:- 找到midVal等于target,返回true;
- midVal<target,目标在右半区,left=mid+1;
- midVal>target,目标在左半区,right=mid-1;
- 循环终止:left>right时,说明target不存在,返回false。
性能说明
- 时间复杂度:O(log(mn))(仅需一次二分,比原解法的O(logm + n)更优,尤其当n较大时);
- 空间复杂度:O(1)(与原解法一致);
- 优势:
- 时间复杂度最优,充分利用矩阵"整体有序"的核心特性;
- 代码简洁,无需分两次查找,逻辑更闭环;
- 劣势:需理解二维坐标与一维索引的映射关系,新手入门门槛略高。
解法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;
}
核心逻辑说明
- 行二分 :与原解法一致,找到目标行后记录为
targetRow; - 行内二分:对目标行的列做二分查找,而非线性遍历,将行内查找的时间复杂度从O(n)降至O(logn);
- 结果验证:行内二分找到target则返回true,否则返回false。
性能说明
- 时间复杂度:O(logm + logn) = O(log(mn))(与解法1理论复杂度一致);
- 空间复杂度:O(1);
- 优势:
- 比原解法更高效,尤其当列数n较大时;
- 逻辑分步清晰,先找行再找列,易调试;
- 劣势:代码量略多于解法1,需两次二分循环。
总结
- 双层定位法(第一次解答):O(logm + n)时间+O(1)空间,逻辑直观,新手易理解;
- 二维转一维二分法:O(log(mn))时间+O(1)空间,最优解,充分利用矩阵整体有序特性;
- 行二分+行内二分法:O(log(mn))时间+O(1)空间,分步清晰,效率与最优解持平;
- 关键技巧 :
- 核心思想:矩阵"每行递增且下一行首>上一行尾" → 整体等价于一维有序数组,可直接用二分查找;
- 坐标映射:二维转一维的核心是
row=idx/n,col=idx%n,是解法1的关键; - 性能选择:小矩阵用原解法即可,大矩阵优先选解法1(一维二分)或解法2(行+列二分)。