哈喽各位,我是前端小L。
欢迎来到我们的二分查找专题第九篇!我们刚从"旋转数组"的"二义性"泥潭(LC 81)中爬出来,今天,我们面对一个看似更复杂的二维矩阵。
但是,请仔细阅读题目的"附加属性":
-
每行中的整数从左到右按升序排列。 (这个很常见)
-
每行的第一个整数大于前一行的最后一个整数。 (这,就是"Aha!"时刻!)
这个第二条属性 ,是出题人送给我们的"大礼包"!它意味着,如果我们把这个二维矩阵,一行一行地"首尾相连",它会变成一个完美、巨大、且完全有序 的一维数组!
[row_0_elements, row_1_elements, row_2_elements, ...]
这道"二维"问题,被我们瞬间"降维 "成了我们专题第一篇(LC 704)的"Hello, World!"问题!
力扣 74. 搜索二维矩阵
https://leetcode.cn/problems/search-a-2d-matrix/

题目分析:
-
输入 :一个
m x n矩阵,具有上述两条"强有序"属性。一个target。 -
目标 :判断
target是否存在。 -
约束:高效。(暗示 O(log(m*n)))
核心洞察: 我们不需要真的创建一个 O(m*n) 的新数组。我们可以"虚拟"地在这个"拉平后"的数组上进行二分查找。
"万能模板"的"虚拟"应用
我们的"左闭右开 [left, right) 万能模板"再次登场!
1. 区间定义 (一维虚拟化)
-
我们的"虚拟数组"总共有
totalElements = m * n个元素。 -
它的索引范围是
[0, m*n - 1]。 -
套用我们的模板,搜索区间
[left, right)就是[0, m * n)。 -
left = 0 -
right = m * n
2. mid 的"二维映射" (关键技巧)
-
mid = left + (right - left) / 2。这个mid是一个0到m*n - 1之间的一维"虚拟索引"。 -
我们如何把它映射回二维矩阵的
(row, col)坐标? -
假设矩阵有
n列(cols)。 -
row = mid / n(整除n得到行号) -
col = mid % n(模n得到列号)
3. 状态转移 (标准模板)
-
得到
mid对应的midVal = matrix[row][col]。 -
if (midVal == target):找到了,return true。 -
else if (midVal < target):目标在右侧,left = mid + 1。 -
else (midVal > target):目标在左侧,right = mid。
代码实现
#include <vector>
using namespace std;
class Solution {
public:
bool searchMatrix(vector<vector<int>>& matrix, int target) {
if (matrix.empty() || matrix[0].empty()) {
return false;
}
int m = matrix.size(); // 行数
int n = matrix[0].size(); // 列数
// 1. 区间定义:[left, right) -> [0, m*n)
int left = 0;
int right = m * n; // 总元素个数,作为开区间
// 2. 循环条件
while (left < right) {
// 3. mid 计算
int mid = left + (right - left) / 2;
// 4. 将一维 mid 映射回二维 (row, col)
int row = mid / n;
int col = mid % n;
int midVal = matrix[row][col];
// 5. 标准的指针移动
if (midVal == target) {
return true;
} else if (midVal < target) {
// 目标在右侧 [mid + 1, right)
left = mid + 1;
} else { // midVal > target
// 目标在左侧 [left, mid)
right = mid;
}
}
// 循环结束,没找到
return false;
}
};
深度复杂度分析
-
时间复杂度 O(log(m*n)):
-
我们的搜索空间是
m * n个元素。 -
每一次循环,都将搜索空间缩小一半。
-
总的比较次数是对
m * n取对数,即 O(log(m*n))。 -
(
log(m*n) = log(m) + log(n),所以写成O(log m + log n)也是完全正确的)。
-
-
空间复杂度 O(1):
- 我们没有创建那个
m*n的虚拟数组,只使用了left,right,mid,m,n,row,col等常数个额外变量。
- 我们没有创建那个
总结
今天,我们通过一次巧妙的"降维 ",把一个"二维"问题,转化成了一个"一维"问题。 row = mid / cols 和 col = mid % cols 这对"映射"公式,是你务必掌握的"魔法"。
但是,请注意! 这个解法之所以成立 ,全拜那条"每行开头大于上行末尾 "的黄金属性所赐。 如果一个二维矩阵,没有 这条属性,仅仅是"每行有序,每列有序",那它就不能被"拉平"了。
在下一篇中,我们将直面那个真正的、无法"降维"的二维矩阵搜索问题 (LC 240),看看那时我们又该如何应对。
下期见!