题目描述
给定M×N矩阵,每一行、每一列都按升序排列,请编写代码找出某元素。
示例:
现有矩阵 matrix 如下:
[
[1, 4, 7, 11, 15],
[2, 5, 8, 12, 19],
[3, 6, 9, 16, 22],
[10, 13, 14, 17, 24],
[18, 21, 23, 26, 30]
]
-
给定 target = 5,返回 true。
-
给定 target = 20,返回 false。
解题方法与思路
-
这道题,确实可以算作一道中等题。它一共有三种解决方案,一种方法的时间复杂度都要低于前面一种的时间复杂度。并且,每一种都有它独特的地方在。我想,这就是这道题的魅力所在。
-
接下来,就让我来为你讲述,这道题的三种方法究竟是哪三种方法。
方法一:顺序查找法
顾名思义,就是顺序查找,暴力破解。我们用两个双层for循环去解决它。
- 但是,需要注意的是,使用一般的双层for循环,这道题会超时,得写成类似于这样的形式才行
for (const auto& row: matrix)
- 这是因为这样的写法,就不会去复制数组里的元素值,从而节省了一定的时间。
具体的代码如下:
class Solution {
public:
bool searchMatrix(vector<vector<int>>& matrix, int target) {
if(matrix.empty()) return false;
for (const auto& row: matrix) {
for (int element: row) {
if (element == target) {
return true;
}
}
}
return false;
}
};
复杂度分析
时间复杂度:O(mn) 其中m是矩阵的行数,n是矩阵的列数
空间复杂度:O(1)
方法二:二分查找法
- 由于这个矩阵中的元素是有序的,所以我们可以使用二分查找法来降低时间复杂度。
- 我们分别对矩阵的每一行使用二分查找,去找目标元素,找到了就返回true,否则就是false
- 二分查找其实就是设置两个指针left 与 right 分别指向数组的头元素与尾元素。然后使用while循环去遍历这整个数组
- left 要小于等于 right 。我们找到数组的中间元素mid。 假如mid 等于目标元素,返回true,小于目标元素,left = mid +1,否则就是right = mid -1
- 最后如果都找不到就返回 false
具体的代码如下:
class Solution {
public:
bool searchMatrix(vector<vector<int>>& matrix, int target) {
for(int i = 0; i < matrix.size(); ++i){
int left = 0;
int right = matrix[0].size() - 1;
while(left <= right){
int mid = (left + right) / 2;
if(matrix[i][mid] == target) return true;
else if(matrix[i][mid] < target) left = mid +1;
else right = mid - 1;
}
}
return false;
}
};
复杂度分析
时间复杂度:O(m * log(n)) 其中m是矩阵的行数,n是矩阵的列数
空间复杂度:O(1)
方法三:基于观察法的查找矩阵算法,Z字查找
- 我们观察矩阵,发现这个矩阵的横向与纵向的元素其实都是递增的。
- 根据这个特性,我们可以从矩阵的右上角元素开始遍历。
- 假如目标元素大于右上角元素,说明这个元素,大于第一行所有元素,下次遍历的时候,就直接少遍历一行,从下一行开始遍历。
- 假如目标元素小于左上角元素,那就说明,这个元素,直接小于最后一列的所有元素,所以下次就直接少遍历一列。
- 由此这样遍历,每一次遍历,都能缩小一行或者一列的元素。进一步的缩小了时间复杂度与空间复杂度。
具体的代码如下:
class Solution {
public:
bool searchMatrix(vector<vector<int>>& matrix, int target) {
if(matrix.empty()) return false;
int row = 0;
int col = matrix[0].size() - 1;
while(row < matrix.size() && col >= 0){
if(matrix[row][col] > target) --col;
else if(matrix[row][col] < target) ++row;
else return true;
}
return false;
}
};
复杂度分析
时间复杂度:在这个解决方案中,我们从右上角开始搜索,并在每次迭代中排除一行或一列。在最坏的情况下,我们可能需要遍历所有的行和列,因此时间复杂度为 O(M+N),其中 M 是矩阵的行数,N 是矩阵的列数。
空间复杂度:这个算法没有使用额外的数据结构来存储数据,只使用了几个整数变量(如 row 和 col),因此空间复杂度为 O(1)。
这个优化方案相对于二分查找方案,在时间复杂度上有所改进,而空间复杂度保持不变。
总结
-
这道题用到了二分查找法与观察法来解决矩阵问题,是一道夯实基础的好题。
-
觉得我写的还行的小伙伴们请给我点一个赞,点一下关注,谢谢!你们的赞与关注就是对我持续输出优质内容的最大鼓励!