剑指 Offer 04. 二维数组中的查找
难度:中等 牛客地址 LeetCode地址
题目描述
在一个二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。
请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。
样例
现有矩阵 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
。
限制
-
m m m 为二维数组的行数, n n n 为二维数组的列数。
-
0 < = m , n < = 1000 0 <= m, n <= 1000 0<=m,n<=1000
算法
法1)标志位法、线性查找、思维
-
由于给定的二维数每行从左到右递增、每列从上到下递增,也就是说,任意给我们一个数,这个数左边的数都比当前数小,下边的数都比当前数大。
-
根据上面的条件,我们仔细想想。不难发现,我们每一次都可以排除部分一定不符合题意的元素。
-
从二维数组的右上角开始查找:
-
- 如果当前元素等于目标值,则返回
true
。
- 如果当前元素等于目标值,则返回
-
- 如果当前元素大于目标值,则当前元素下面的所有元素都一定严格大于目标值,即当前列不存在目标值,因此当前列可以排除,故往左移一列。
-
- 如果当前元素小于目标值,则当前元素左面的所有元素都一定严格小于目标值,即当前行不存在目标值,因此当前行可以排除,故往下移一行。
-
以此类推,我们每一次都可以排除一行或者一列元素,直至找到目标值,或者行下标或列下标超出边界。
时间复杂度
-
查询的行数最多为 m m m 行,查询的列数最多为 n n n 列,因此最多会查询 m + n m + n m+n 次。
-
故时间复杂度为 O ( m + n ) O(m + n) O(m+n) 。
空间复杂度
- 额外空间复杂度为 O ( 1 ) O(1) O(1) 。
C++代码
class Solution {
public:
bool findNumberIn2DArray(vector<vector<int>>& matrix, int target) {
if (matrix.empty() || matrix[0].empty()) return false;
int m = matrix.size(), n = matrix[0].size(), i = 0, j = n - 1;
while (i < m && j >= 0) {
if (matrix[i][j] == target) return true;
else if (matrix[i][j] > target) --j;
else ++i;
}
return false;
}
};
分析总结:
此方法称为标志位法,我们必须从四个角开始入手,但是也不是所有的角都可以入手的:
-
左上角:右边递增,下边递增,不能选。
-
左下角:右边递增,上边递减,能选。
-
右上角:左边递减,下边递增,能选。
-
右下角:左边递减,上边递减,不能选。
不难发现,只有两个方向上变化不同的角落才是我们可以选择的角,这样我们才可以达到标记的效果,才可以:无论当前值比目标值大还是小,我们都可以排除部分元素,保留部分元素,达到查找的效果。
附从左下角开始查找的C++代码
class Solution {
public:
bool findNumberIn2DArray(vector<vector<int>>& matrix, int target) {
if (matrix.empty() || matrix[0].empty()) return false;
int m = matrix.size(), n = matrix[0].size(), i = m - 1, j = 0;
while (i >= 0 && j < n) {
if (matrix[i][j] == target) return true;
else if (matrix[i][j] > target) --i;
else ++j;
}
return false;
}
};
法2)二分查找
- 舍弃每一列都按照从上到下递增的顺序排序这一条件,根据每一行都按照从左到右递增的顺序排序这一条件逐行二分查找目标值。
时间复杂度
- 最坏需要遍历 m m m 行。
- 每一行需要二分 l o g n logn logn 次。
- 故时间复杂度为 O ( m ∗ l o g n ) O(m*logn) O(m∗logn) 。
空间复杂度
- 额外空间复杂度为 O ( 1 ) O(1) O(1) 。
C++代码
class Solution {
public:
bool findNumberIn2DArray(vector<vector<int>>& matrix, int target) {
if (matrix.empty() || matrix[0].empty()) return false;
int m = matrix.size(), n = matrix[0].size(), i = 0, j = n - 1;
for (int i = 0; i < m; i ++ ) {
auto t = lower_bound(matrix[i].begin(), matrix[i].end(), target);
if (t != matrix[i].end() && *t == target) return true;
}
return false;
}
};
分析总结:
同理,我们可以舍弃每一行都按照从左到右递增的顺序排序这一条件,根据每一列都按照从上到下递增的顺序排序这一条件逐列二分查找目标值,代码和上述代码类似,此处不再赘述。
混一天和努力一天,一天看不出任何差别,三天看不到任何变化,七天也看不到任何距离……但是一个月后会看到话题不同,三个月后会看到气场不同,半年后会看到距离不同,一年后会看到人生道路截然不同。
————送给每个仍在为梦想努力奋斗的你