📝 题目描述

题目链接搜索二维矩阵

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

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

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

示例:

示例 1:

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

示例 2:

1
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^4 <= matrix[i][j], target <= 10^4

💡 解题思路

方法一:两次二分查找

由于每行的第一个元素大于前一行的最后一个元素,且每行元素是升序的,所以每行的第一个元素大于前一行的第一个元素,因此矩阵第一列的元素是升序的。

我们可以对矩阵的第一列的元素二分查找,找到最后一个不大于目标值的元素,然后在该元素所在行中二分查找目标值是否存在。

方法二:一次二分查找

若将矩阵每一行拼接在上一行的末尾,则会得到一个升序数组,我们可以在该数组上二分找到目标元素。

代码实现时,可以二分升序数组的下标,将其映射到原矩阵的行和列上。

🔧 代码实现

1、两次二分查找

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Solution {
public:
bool searchMatrix(vector<vector<int>> matrix, int target) {
// 找到第一个大于 target 的元素,因此当大于 target 时,返回 true
vector<vector<int>>:: row = upper_bound(matrix.begin(), matrix.end(), target, [](const int target, const vector<int> &vec) {
return vec[0] > target;
});
if (row == matrix.begin()) {
return false;
}
--row;
// row 是迭代器,表现得像指针,row->begin() 实际上是在调用该行的第一个元素的迭代器
return binary_search(row->begin(), row->end(), target, less<int>());
}
};

2、一次二分查找

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Solution {
public:
bool searchMatrix(vector<vector<int>>& matrix, int target) {
int m = matrix.size(), n = matrix[0].size();
int low = 0, high = m * n - 1;
while (low <= high) {
int mid = low + (high - low) / 2;
// 通过整除和取余获取坐标
int x = matrix[mid / n][mid % n];
if (x < target) {
low = mid + 1;
} else if (x > target) {
high = mid - 1;
} else {
return true;
}
}
return false;
}
};

📊 复杂度分析

1、两次二分查找

  • 时间复杂度O(logm+logn)=O(logmn)O(logm+logn)=O(logmn),其中 m 和 n 分别是矩阵的行数和列数。
  • 空间复杂度O(1)O(1)

2、一次二分查找

  • 时间复杂度O(logmn)O(logmn),其中 m 和 n 分别是矩阵的行数和列数。
  • 空间复杂度O(1)O(1)

🎯 总结

  • 核心思想:主要学会 upper_boundbinary_search 的用法。