![7a79d3cfc6bceb74cd88de2ec37c806d.png](https://img-blog.csdnimg.cn/img_convert/7a79d3cfc6bceb74cd88de2ec37c806d.png)
leetcode 第 74 号题
在看剑指 offer 第 4 号题之前,我们先来看一个类似的二维数组搜索的问题。这道题目是 leetcode 的 74 号题目,问题描述如下。
编写一个高效的算法来判断 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
这道题目最直接的方式就是暴力的将目标值和二维数组中的每个元素值进行比较,看看是否存在相等的元素,代码如下:
public
以上代码的复杂度分析是:
- 时间复杂度是 O(n^2)
- 空间复杂度是 O(1)
显然,上面的解法效率不高,为了提高算法的效率,我们需要利用上给定矩阵的特点,也就是有序这个特点,如果我们将上面的二维矩阵转化成一维矩阵,你会发现这个一维矩阵是有序的,如下图:
![0c0af7223656eb5678215a77d7abf4d9.png](https://img-blog.csdnimg.cn/img_convert/0c0af7223656eb5678215a77d7abf4d9.png)
做了这一层转换后,现在的问题就变成了:在一个排好序的数组中查找目标值是否存在,这个问题的最高效的解决方案是二分查找了。代码如下:
public
以上代码的复杂度分析是:
- 时间复杂度是 O(log(n))
- 空间复杂度是 O(1)
这才是这道题的最佳答案。
剑指 offer 第 4 号题目
接下来我们看看剑指 offer 第 4 号题目,这个题目和上面的题目还是有点差别的。
题目描述
编写一个高效的算法来判断 m x n 矩阵中,是否存在一个目标值。该矩阵具有如下的特性:
- 每行中的整数从左到右按升序排列
- 每列中的整数从上到下按升序排列 (这里就和上面的题目不同了)
示例 1:
输入:
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
示例 2:
输入:
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 = 20
输出: false
方案一:暴力解法
和上面的题目一样,这道题也可以采用暴力解法 (如果在面试的时候,一开始没思路的时候,你可以先将暴力解法说出来,总比做不出来强)。代码如下:
public
以上代码的复杂度分析是:
- 时间复杂度是 O(n^2)
- 空间复杂度是 O(1)
方案二:二分查找
我们现在回过头看看这道题,这道题给的数组是有顺序的,虽然不像前面第一道题那样转成一维数组的顺序,至少这道题的数组的每一行和每一列都是有顺序的,既然想在有序的数组中查找某个目标值,那么我们就可以使用二分查找,我们看下面的视频讲解。
知乎视频www.zhihu.com代码如下:
public
复杂度分析:
- 空间复杂度是 O(1)
- 时间复杂度是 O(log(n!)),这个复杂度计算的逻辑看下面:
在主循环中,每次循环都会执行两次二分查找算法,每次二分查找的元素的个数分别是 (m - i) 和 (n - i),其中 m 表示行数,n 表示列数,i 表示当前遍历的行或者列,所以每次循环的时间复杂度是 O(log(m - i) + log(n - i))。
当 m 和 n 差不多大的时候,是最坏的情况,可以把上面的时间复杂度简化为 O(2log(n - i)) = O(log(n - i)),这个是一次循环迭代的时间复杂度,那么所有的循环次数加起来总的时间复杂度是:
O(logn + log(n - 1) + log(n - 2) + ...+ log(n - i) + ... + log1)
然后,我们根据对数的乘法法则:log(a) + log(b) = log(ab),得到上面的时间复杂度等于:
O(log(n*(n - 1)*(n - 2)*...*(n - i)*...*1)) = O(log(n!))
方案三:线性查找
如果二分查找是利用了数组有序的特点,那么接下来使用的线性查找方法就更加的利用了这道题的数组的特点:
- 左下角的元素值大于它上面的元素,而小于它右边的元素
- 右上角的元素值大于它左边的元素,而小于它下面的元素
利用这个特点我们就可以实现线性查找来解决这个问题,看下面的视频讲解。
知乎视频www.zhihu.com代码如下:
public
复杂度分析如下:
- 空间复杂度是 O(1)
- 时间复杂度是 O(m + n),最坏的情况是搜索了 m 行再加 n 列还没有查找到目标元素。