题目理解
matrix 是一个 行排序矩阵,即:
• 每一行的元素从 左到右递增。
• 每一行的 最右元素 一定大于等于该行所有元素,且小于等于下一行的所有元素(如果有下一行)。
目标:判断 target 是否在 matrix 中。
解法思路
我们采用 两阶段二分查找 进行优化:
1. 第一步:遍历每一行,找到可能包含 target 的行。
• 如果 target 大于 当前行的最后一个元素,则 target 只能在后面的行,继续下一行。
• 否则,该行 可能包含 target,进入 第二步。
2. 第二步:在该行使用 二分查找 搜索 target。
• 如果找到,返回 true。
• 否则,返回 false(因为 target 只可能在当前行,不在其他行)。
代码解析
class Solution {
public:
bool searchMatrix(vector<vector<int>>& matrix, int target) {
int n = matrix.size(); // 矩阵行数
int m = matrix[0].size(); // 矩阵列数
for (int i = 0; i < n; i++) { // 遍历每一行
if (target > matrix[i][m - 1]) continue; // `target` 大于该行最后一个元素,跳过
// 二分查找
int left = 0, right = m - 1;
while (left <= right) {
int mid = left + (right - left) / 2; // 防止溢出
if (matrix[i][mid] == target) return true; // 找到 `target`
else if (matrix[i][mid] > target) right = mid - 1; // 缩小右边界
else left = mid + 1; // 缩小左边界
}
break; // 只可能在这一行找到,找不到就退出
}
return false; // 遍历完所有可能行,仍找不到
}
};
详细运行步骤
示例 1
vector<vector<int>> matrix = {
{1, 3, 5, 7},
{10, 11, 16, 20},
{23, 30, 34, 60}
};
int target = 3;
矩阵表示:
1 3 5 7
10 11 16 20
23 30 34 60
执行过程
第一步:寻找目标行
• 遍历第 0 行,最后元素 7:
• target = 3 ≤ 7,可能在当前行,进入 二分查找。
第二步:二分查找
• left = 0, right = 3, mid = (0+3)/2 = 1
• matrix[0][1] = 3 == target,返回 true。
✅ 最终结果:true
示例 2
vector<vector<int>> matrix = {
{1, 3, 5, 7},
{10, 11, 16, 20},
{23, 30, 34, 60}
};
int target = 13;
第一步:寻找目标行
• 第 0 行:7 < 13,跳过。
• 第 1 行:20 ≥ 13,进入 二分查找。
第二步:二分查找
• left = 0, right = 3, mid = 1
• matrix[1][1] = 11 < target → left = mid + 1 = 2
• left = 2, right = 3, mid = 2
• matrix[1][2] = 16 > target → right = mid - 1 = 1
• left > right,找不到,返回 false。
❌ 最终结果:false
示例 3
vector<vector<int>> matrix = {
{1, 3, 5, 7},
{10, 11, 16, 20},
{23, 30, 34, 60}
};
int target = 60;
第一步:寻找目标行
• 第 0 行:7 < 60,跳过。
• 第 1 行:20 < 60,跳过。
• 第 2 行:60 ≥ 60,进入 二分查找。
第二步:二分查找
• left = 0, right = 3, mid = 1
• matrix[2][1] = 30 < target → left = mid + 1 = 2
• left = 2, right = 3, mid = 2
• matrix[2][2] = 34 < target → left = mid + 1 = 3
• left = 3, right = 3, mid = 3
• matrix[2][3] = 60 == target,返回 true。
✅ 最终结果:true
复杂度分析
时间复杂度
1. 遍历行:O(N)
• 最坏情况,可能要遍历所有行。
• 最优情况,第一行就满足条件。
2. 二分查找:O(log M)
• 在选定的一行内,二分查找 O(log M)。
总复杂度:
通常 M 很大时,log M 远小于 N,所以二分查找优化明显。
空间复杂度
• 只使用了常数变量 left、right、mid,不需要额外的数组。
优化建议
当前算法在最坏情况下需要遍历所有行。可以使用 一次性二分查找 代替两次:
1. 把矩阵视为一个一维数组,索引映射:
2. 直接对整个矩阵二分查找。
class Solution {
public:
bool searchMatrix(vector<vector<int>>& matrix, int target) {
int n = matrix.size(), m = matrix[0].size();
int left = 0, right = n * m - 1;
while (left <= right) {
int mid = left + (right - left) / 2;
int val = matrix[mid / m][mid % m]; // 计算对应的行列索引
if (val == target) return true;
else if (val > target) right = mid - 1;
else left = mid + 1;
}
return false;
}
};
时间复杂度:O(log (N * M))
空间复杂度:O(1)
这个方法比你的方法 更快,适用于 行列均递增的矩阵。
总结
✅ 你的方法
• 两步二分(O(N + log M))。
• 适用于行递增的矩阵。
✅ 优化方案
• 一维二分(O(log (N*M)),更快)。
• 适用于行列均递增的矩阵。
👉 如果 matrix 行列都递增,建议用 O(log(N*M)) 版本!