一、前言
大家好,本文章属于《剑指 Offer 每日一题》中的系列文章中的第 2 篇。
在该系列文章中我将通过刷题练手的方式来回顾一下数据结构与算法基础,同时也会通过博客的形式来分享自己的刷题历程。如果你刚好也有刷算法题的打算,可以互相鼓励学习。我的算法基础薄弱,希望通过这两三个月内的时间弥补这块的漏洞。本次使用的刷题语言为 Java ,预计后期刷第二遍的时候,会采用 Python 来完成。
- 刷题平台: leetcode 的剑指 Offer 专题
- 码云仓库地址
二、题目
题目描述:
在一个 n * m 的二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个高效的函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。
示例:
现有矩阵 arr 如下:
[
[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
限制:
0 <= n <= 1000
0 <= m <= 1000
复制代码
三、解题思路
3.1 思路1:暴力法
子开始最容易想到的方法便是暴力法,即循环遍历二维数组,逐个与目标值比较,这种方式就没有利用到题目给的数组有序这个信息,但是它简单呀,下面我们来看看这种方式的效果如何
3.1.1 代码实现
Java 代码实现:
// 方法一:暴力法遍历数组
public static boolean findNumber(int [][] arr,int target){
// arr.lenth 代表有多少列
for (int i=0;i<arr.length;i++){
for (int j = 0; j < arr[0].length; j++) {
if(arr[i][j] == target){
return true;
}
}
}
return false;
}
复制代码
3.1.2 执行效率
3.1.3 复杂度分析
显然时间复杂度为O(n2),空间复杂度为O(1)
3.2 思路2:坐标轴法
这种方法的实现是充分抓住了题目中给的有序的信息,
- 1、从左到右递增
- 2、从上到下递增
这种情况造成的数据规律就是 我们如果从数组的左上角开始找,那么如果当前数值比目标值小的话,向下走或者向右走都可以让数字变大,但是方向不能确定。
所以我们应该从右上角或者左下角出发。以右上角为例:
- 1、如果当前数的值比目标值大,往左找。
- 2、如果当前数的值比目标值小,往下找。
这就十分地像一棵二叉搜索树。
举例:比如在下面的二维数组中查找数字7 ,查找的过程如下:
3.2.1 代码实现
Java 代码实现:
// 方式二:二叉搜索树
public static boolean findNumber2(int [][] arr,int target){
// 此时数组为空
if(arr.length ==0){
return false;
}
int i=arr.length-1,j=0;
while (i>=0 && j< arr[0].length){
if(arr[i][j] >target)
i--;
else if(arr[i][j]<target)
j++;
else
return true;
}
return false;
}
复制代码
3.2.2 执行效果
3.2.3 复杂度分析
- 时间复杂度为O(M+N),此算法最多循环 M+N次,M和N 代表二位数组的列数和行数
- 空间复杂度为O(1),使用了 i、j常数大小的额外空间。
四、小结
本题考查了对二维数组的理解和编程能力,二维数组在内存中占据连续的空间,我们一般通过 arr.length 来获取二维数组的行数,然后通过arr[0].length 来获取二维数组的列数。
同时,除了暴力法外,我们要充分利用题目中的条件,例如本题中从右上角看类似于一棵二叉搜索树,这也是突破问题的关键。
好了,今天的刷题就到这了,欢迎点赞评论交流,剑指 Offer 刷题之旅将继续展开!