1. 题目来源
链接:搜索二维矩阵
来源:LeetCode
2. 题目说明
编写一个高效的算法来判断 m x n
矩阵中,是否存在一个目标值。该矩阵具有如下特性:
- 每行中的整数从左到右按升序排列。
- 每行的第一个整数大于前一行的最后一个整数。
示例 1:
输入:
matrix = [
[1, 3, 5, 7],
[10, 11, 16, 20],
[23, 30, 34, 50]
]
target = 3
输出: true
示例 2:
输入:
matrix = [
[1, 3, 5, 7],
[10, 11, 16, 20],
[23, 30, 34, 50]
]
target = 13
输出: false
3. 题目解析
3.1 方法一:暴搜
这是个没有营养的解答,会浪费你 10 秒钟~~~
// 执行用时 :12 ms, 在所有 C++ 提交中击败了53.83%的用户
// 内存消耗 :10.1 MB, 在所有 C++ 提交中击败了5.29%的用户
class Solution {
public:
bool searchMatrix(vector<vector<int>>& matrix, int target) {
if (matrix.empty() || matrix[0].empty())
return false;
for (int i = 0; i < matrix.size(); ++i) {
for (int j = 0; j < matrix[0].size(); ++j) {
if (target == matrix[i][j])
return true;
}
}
return false;
}
};
3.2 方法二:朴素二分法、细节处理
题目很明确,矩阵元素有序,就是思考如何二分,思路如下:
- 如果是查找第一个不小于目标值的数,当
target
在第一列时,会返回target
所在的行,但若target
不在的话,有可能会返回下一行,不好统一 - 故可以查找第一个大于目标值的数,这样只要回退一个,就一定是
target
所在的行。但需要注意的一点是,如果返回的是 0,就不能回退了,以免越界,记得要判断一下 - 找到了
target
所在的行数,就使用二分法即可
参见代码如下:
// 执行用时 :12 ms, 在所有 C++ 提交中击败了53.83%的用户
// 内存消耗 :9.8 MB, 在所有 C++ 提交中击败了22.75%的用户
class Solution {
public:
bool searchMatrix(vector<vector<int>>& matrix, int target) {
if (matrix.empty() || matrix[0].empty())
return false;
int left = 0, right = matrix.size();
while (left < right) {
int mid = (left + right) / 2;
if (matrix[mid][0] == target)
return true;
if (matrix[mid][0] <= target)
left = mid + 1;
else
right = mid;
}
int tmp = (right > 0) ? (right - 1) : right;
left = 0;
right = matrix[tmp].size();
while (left < right) {
int mid = (left + right) / 2;
if (matrix[tmp][mid] == target)
return true;
if (matrix[tmp][mid] < target)
left = mid + 1;
else
right = mid;
}
return false;
}
};
3.2 方法二:依据坐标关系二分法
这道题也可以使用一次二分查找法:
- 按S型遍历该二维数组,可以得到一个有序的一维数组,只需要用一次二分查找法
- 关键在于坐标的转换,如何把二维坐标和一维坐标转换是关键点:
- 把一个长度为
n
的一维数组转化为m * n
的二维数组(m * n = n)
后,那么原一维数组中下标为i
的元素将出现在二维数组中的[i / n][i % n]
的位置,有了这一点
参见代码如下:
// 执行用时 :4 ms, 在所有 C++ 提交中击败了99.52%的用户
// 内存消耗 :10 MB, 在所有 C++ 提交中击败了5.29%的用户
class Solution {
public:
bool searchMatrix(vector<vector<int>>& matrix, int target) {
if (matrix.empty() || matrix[0].empty())
return false;
int m = matrix.size(), n = matrix[0].size();
int left = 0, right = m * n;
while (left < right) {
int mid = (left + right) / 2;
if (matrix[mid / n][mid % n] == target)
return true;
if (matrix[mid / n][mid % n] < target)
left = mid + 1;
else
right = mid;
}
return false;
}
};
3.3 双指针法
这道题其实也可以不用二分搜索法,直接使用双指针也是可以的:
i
指向 0,j
指向列数,这样第一个被验证的数就是二维数组右上角的数字- 若等于
target
,直接返回true
- 若大于
target
,说明要减小数字,则列数j
自减 1 - 若小于
target
,说明要增加数字,行数i
自增 1 - 若
while
循环退出了还是没找到target
,直接返回false
即可
参见代码如下:
// 执行用时 :0 ms, 在所有 C++ 提交中击败了100.00%的用户
// 内存消耗 :10 MB, 在所有 C++ 提交中击败了5.29%的用户
class Solution {
public:
bool searchMatrix(vector<vector<int>>& matrix, int target) {
if (matrix.empty() || matrix[0].empty())
return false;
int i = 0, j = (int)matrix[0].size() - 1;
while (i < matrix.size() && j >= 0) {
if (matrix[i][j] == target)
return true;
else if
(matrix[i][j] > target) --j;
else
++i;
}
return false;
}
};