目录
引言
这一题刚好还使用到了上一个二分搜索题目的技巧,就是二分法结束后,left左边界指向的是第一个大于等于目标值元素的索引。
搜索二维矩阵
- 🎈 题目链接:
- 🎈 做题状态:
我的解题
首先用二分法遍历矩阵第一列数据确定所在行,然后再遍历目标行确定所在列。
class Solution {
public:
bool searchMatrix(vector<vector<int>>& matrix, int target) {
// 首先用二分法确定所在行,然后再确定所在列
int rows = matrix.size();
int cols = matrix[0].size();
int left = 0; // 左边界
int right = rows - 1; // 右边界
// 遍历第一列找到目标行,需要找到第一个大于或等于 target 的行
while (left <= right)
{
int mid = (right - left) / 2 + left;
if (matrix[mid][0] > target)
{
// 大于目标值,移动右边界
right = mid - 1;
} else if (matrix[mid][0] < target)
{
// 小于目标值,移动左边界
left = mid + 1;
}
else
{
// 等于目标值
return true;
}
}
// left-1 就是目标行,left指向第一个大于目标值的位置
int targetRow = left - 1;
if (targetRow < 0) return false; // 要注意这个边界情况,可能会越界!!
left = 0;
right = cols - 1;
while(left <= right)
{
int mid = (right - left) / 2 + left;
if (matrix[targetRow][mid] > target)
{
// 大于目标值,移动右边界
right = mid - 1;
} else if (matrix[targetRow][mid] < target)
{
// 小于目标值,移动左边界
left = mid + 1;
}
else
{
// 等于目标值
return true;
}
}
return false;
}
};
代码优化
思路清晰:
- 先在矩阵的首列中用二分查找确定「目标可能在哪一行」;
- 然后在这一行中用二分查找找「目标是否存在」。
这正是这道题目最经典的“两阶段二分法”解法,性能是 O(log m + log n)
。
✅ 优点总结:
点 | 表现 |
---|---|
思路清晰 | 两段二分逻辑拆得很干净 |
边界判断严谨 | 能判断 targetRow < 0 防止越界 |
变量命名明确 | 逻辑自洽,可读性强 |
能通过所有测试 | 完全符合 LeetCode 要求 |
✅ 但还有一些可以轻度优化或提升可读性的地方:
🔧 优化建议 1:二分第一列可以换思路(不找第一个 ≥ target,而是找最后一个 ≤ target)
你现在的代码是:
while (left <= right) {
...
}
// left - 1 就是目标行
int targetRow = left - 1;
这段虽然正确,但可以写得更直观一些,比如直接写成找 最后一个 ≤ target 的行,让语义更明确。
不过保留你原写法也没问题,因为你注释写得清楚,不影响理解。
🔧 优化建议 2:二段式二分可合并为一段式
还有一种更极致优化(不一定需要改)的做法是——把整个二维矩阵当成一维数组处理,只做一次二分查找:
class Solution {
public:
bool searchMatrix(vector<vector<int>>& matrix, int target) {
// 首先用二分法确定所在行,然后再确定所在列
int rows = matrix.size();
int cols = matrix[0].size();
int left = 0; // 左边界
int right = rows * cols - 1; // 右边界
// 遍历第一列找到目标行,需要找到第一个大于或等于 target 的行
while (left <= right)
{
int mid = (right - left) / 2 + left;
if (matrix[mid / cols][mid % cols] > target) // 坐标映射是关键点,一个是除以,一个是取余数。
{
// 大于目标值,移动右边界
right = mid - 1;
} else if (matrix[mid / cols][mid % cols] < target)
{
// 小于目标值,移动左边界
left = mid + 1;
}
else
{
// 等于目标值
return true;
}
}
return false;
}
};
✅ 优点:
- 一次二分,代码更短;
- 时间复杂度仍为
O(log(m*n))
,更紧凑。
⚠️ 缺点:
- 可读性略差一点,不如你那种“先找行再找列”直观;
- 对初学者不够友好。
📌 总结评价:
方面 | 你的写法 | 优化建议 |
---|---|---|
正确性 | ✅ 完美 | ✅ 一样正确 |
可读性 | ✅ 强(注释清晰) | ⬆️ 可略简化 |
性能 | ✅ O(log m + log n) | ✅ 可合并为 O(log mn) |
工程推荐 | ✅ 很推荐 | ✅ 合并写法适合追求极简的情况 |