剑指Offer——二维数组中的查找

题目链接

https://www.nowcoder.com/practice/abc3fe2ce8e146608e868a70efebf62e?tpId=13&tqId=11154&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking

 

题目

剑指Offer——二维数组中的查找_第1张图片

剑指Offer——二维数组中的查找_第2张图片

 

函数接口

剑指Offer——二维数组中的查找_第3张图片

 

题解

思路一

拿到这个题,最简单的思路就是数组遍历,如果存在该数,直接返回true。否则在方法结束前返回false。

虽然一开始否了这个想法,因为复杂度太高(O(M*N))。

但是后看别人的题解,发现可以直接遍历。所以写了一份代码放在这里。(不得不说这道题的样例规模确实小)

/*
 * 思路:for循环遍历二维数组
 * 时间复杂度:O(M*N)
 * 
 * */
public class Solution {
    public boolean Find(int target, int [][] array) {
        for(int[] i: array){
            for(int j: i){
                if(j==target) return true;
            }
        }
        return false;
     }
}

 

思路二

是笔者完成这道题是使用思路二,核心的想法就是二分。

最先想到的是同时对二维数组的行列进行二分,从而将该二维数组划分为四个象限,再利用循环和递归进一步划分,从而找到解。

然而在准备编码时,发现对行列同时二分会使得进一步寻找出现“岔路”。例如下方这个4*4的二维数组。

剑指Offer——二维数组中的查找_第4张图片

对行列同时进行二分时,得到的行中间值=2,列中间值=2。即是10这个数。如果我们需要查找的数是6。那么就可以走7或者9这两个方向。可以预见,当二维数组越大,这样的二分方式会导致越来越多的分支情况。不仅判断复杂,而且不便于维护。

所以笔者放弃了这个思路。

同时由于这个题目的特性,即同一行、同一列都是单调非降的,也就是说在单独的一行或者一列进行二分查找是可行的。所以笔者最终采用了一开始思路的退化版本——即遍历行,同时对每一行进行二分查找。这样就将复杂度降低到了线性对数级(O(nlogn))。

下面给出思路二的代码

/*
 * 思路:遍历行,每行进行二分查找
 * 时间复杂度:O(MlongN)
 * 
 * */
import java.util.Arrays;
public class Solution {
    public boolean Find(int target, int [][] array) {
	for(int[] i : array){
		if(Arrays.binarySearch(i, target) >= 0) return true;
	}
	return false;
    }
}

需要注意的是,上述代码中的二分查找是使用数组帮助类Arrays自带的二分查找方法Arrays.binarySearch(arr, key);所以需要导包,不然会报错。

 

思路三

除了上述的思路,笔者在查看其他提交者的代码时,发现了一种更为优异的算法。下面通过图片来说明该算法。

数组为int array[][] = { { 1, 2, 8, 9 }, { 2, 4, 9, 12 }, { 4, 7, 10, 13 }, { 6, 8, 11, 15 } };

需要在其中查找9这个元素

剑指Offer——二维数组中的查找_第5张图片

数组的初始状态如上。

首先说明一个显而易见的事实:由于该二维数组中所有行列都是单调非减的,所以若需要查找的数小于该行/列的行首/列首,那么就说明该数不在该行/列中,同时该数可能存在与该行/列的前一行/列中;反之,若需要查找的数大于该行/列的行尾/列尾,那么就说明该数不在该行/列中,同时该数可能存在与该行/列的后一行/列中。

同时由于上文中提到的“岔道”的概念,若从左上角或者右下角来进行查找,有大概率会出现“岔道”。所以只能从左下或右上进行查找。

下面通过图片继续说明该算法。

剑指Offer——二维数组中的查找_第6张图片

从左下角进行查找,由于6<9,所以应该去同行的下一列查找。

剑指Offer——二维数组中的查找_第7张图片

在下一列中,由于8<9,所以继续去同行的下一列查找

剑指Offer——二维数组中的查找_第8张图片

在下一列中,由于11>9,又因为该行单调非减,所以该行后面的列中不可能再出现9。故继续遍历该行没有意义,程序返回上一行中进行查找。

剑指Offer——二维数组中的查找_第9张图片

在倒数第二行中,由于10>9,同理该行也不可能出现9,继续向上一行进行查找。

剑指Offer——二维数组中的查找_第10张图片

最终,根据该思想,在第二行第三列中找到需要查找的元素9。程序返回true。

下面,给出思路三的代码

/*
 * 思路:根据该数组性质,有选择的忽略某些行列
 * 时间复杂度:O(M+N)
 * 
 * */
public class Solution {
    public boolean Find(int target, int [][] array) {
        int row = array.length, col = array[0].length;
	int rowi = row - 1, coli = 0;
	while (rowi >= 0 && coli < col) {
            if (target > array[rowi][coli]) {
	        coli++;
	    } else if (target < array[rowi][coli]) {
                rowi--;
	    } else {
	        return true;
	    }
	}
	return false;
    }
} 

对于该题,笔者目前理解的就是这三种思路。如果有更好的想法,欢迎发布在评论区一起探讨。

你可能感兴趣的:(剑指Offer)