剑指Offer——二维数组中的查找
二维数组中的查找
https://www.nowcoder.com/practice/abc3fe2ce8e146608e868a70efebf62e?tpId=13&&tqId=11154&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking
题目描述
在一个二维数组中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。
示例
输入 7,[[1,2,8,9],[2,4,9,12],[4,7,10,13],[6,8,11,15]] 返回值 true
题解
- 暴力法:遍历数组,O(n^m)
- 二分法:将每行或每列当做n或m个递增数组,分别进行二分查找。O(n*logm)
public class Solution { public boolean Find(int target, int [][] array) { for(int row=0;row<array.length;++row){ int left=0; int right=array[row].length-1; while(left<=right){ int mid=(left+right)/2; if(array[row][mid]>target){ right=mid-1; }else if(array[row][mid]<target){ left=mid+1; }else{ return true; } } } return false; } }
- 二分变形法
- 一维递增数组二分:基准小看右边,基准大看左边。
- 基准:左、右、中间、随机。
- 二维递增数组二分
- 基准:左上角(下大、右大,无法确定方向,放弃)、左下角(上小、右大,可以排除当前列或行,可以)、右上角(左小、下大,可以排除当前列,可以)、右下角(上小、左小,无法确定方向,放弃)、中间(左上小,右下大,每次能排除1/4,但是较复杂)
- 一维递增数组二分:基准小看右边,基准大看左边。
以右上角为例:
- target小,则当前列排除
- target大,则当前行排除
public class Solution { public boolean Find(int target, int [][] array) { int row=0; int cell=array[0].length-1; while(row<array.length&&cell>=0){ if(target==array[row][cell]){ return true; } if(target<array[row][cell]){ cell--; }else{ row++; } } return false; } }
启发
- 解题是第一位:最暴力的方法也是最直接的方法。
- 利用题目的特点:本题的特点是向右、向下递增+查找。由递增+查找可以引出“二分查找”,二分查找通常针对的是一维数组,因此直觉是进行多次二分查找。
- 多次二分查找只用到了一个方向的递增特点。因此可以假设还有更优方法。这时可以寻求互斥的特点:本题互斥的特点就是大与小、上与下、左与右。因为是二维数组,所以需要同时兼顾水平和垂直两个方向,因此可以结合上与右、下与左。从而可以从左下角或右上角分析:行或列要么大、要么小,夹角不确定,看似还是无法确定target的位置,但是可以排除行或列。