数据结构与算法--数组:二维数组中查找

数组
  • 数组最简单的是数据结构,占据一整块连续的内存并按照顺序存储数据,创建数组时候,我们需要首先指定数组的容量大小,然后根据大小分配内存。即使我们只在数组中存储一个元素,亚需要为所有数据预先分配内存,因此空间使用效率差,经常会有空闲区域没有得到充分利用。
  • 由于数组中内存是连续的,于是可以根据下标在O(1)时间内读/写任何元素,因此时间效率很高。
  • 为解决数组空间效率问题,人们设计实现了多种动态数组,Java中的vector。为了避免浪,我们先为数组开辟一个较小的空间,然后向数组添加元素的过程中,当元素个数超过数组容量的时候,我们会重新分配一个更大的空间,接着吧之前的数据复制到新的驻足中,在将之前的内存释放,这样就能减少内存的浪费。但是每一层扩充数组容量都有大量的额外操作,这多时间性能有负面的影响,因此使用动态数组时候要尽量减少改变数组容量大小的次数。
算法题:二维数组中查找
  • 题目:在一个二维数组中,每一行从左到右递增顺序存储,每一列都按照从上到下递增顺序排序。完成一个函数,输入这样一个二维数组和一个整数,输出数组中是否包含改整数以及改整数的位置。
  • 分析过程如下: 例如下面二维数组,每行,列都递增。如果这个数组中查找数字6,则返回true,位置(2,1)。如果查找数字5返回false

1 2 3 4 5
2 4 6 8 10
3 6 9 12 15
4 8 12 16 20

错误解法分析过程如下
  • 按照题目要求,每列,行都递增,如上,通过观察以上矩阵的特性,很多同学会采用二分法,步骤如下:
    • 将二维数组看成是一个现象的结构,总共有20个元素,left=0,right=20, mid= (left+right)/2=10
    • 取中间位置元素值value与目标元素key比较
    • value == key 则得出结果
    • value < key则left = mid + 1
    • value > key 则right = mid - 1
    • 具体实现如下
/**
 * 错误案例
 * @author liaojiamin
 * @Date:Created in 13:59 2020/10/30
 */
public class FindOne {
    public static Integer[][] getTwoDimenArray(int rows, int cloumns){
        Integer[][] twoDimen = new Integer[rows][cloumns];
        for (int i = 0; i < rows; i++) {
            for (int j = 0; j < cloumns; j++) {
                Integer key = (i+1) * (j+1);
                twoDimen[i][j] = key;
                System.out.print(key + "  ");
            }
            System.out.println("");
        }
        return twoDimen;
    }

    public static void findOneInTwoDimenArray1(int rows, int cloumns, Integer key){
        if(rows <=0 || cloumns <=0){
            return;
        }
        Integer[][] twoDimen = getTwoDimenArray(rows, cloumns);
        if(twoDimen == null){
            return;
        }
        boolean isInArray = false;
        int keyCloumns = -1;
        int keyRows = -1;

        int left = 0;
        int right = rows * cloumns -1;
        int col = cloumns;
        while (left <= right){
            int mid = (left + right) / 2;
            Integer value = twoDimen[mid/ col][mid%col];//中间位置
            if(value == key){
                keyRows = mid/col;
                keyCloumns = mid%col;
                isInArray = true;
                break;
            }else if(value < key){
                left = mid + 1;
            }else {
                right = mid - 1;
            }
        }
        System.out.println("isInArray: "+ isInArray + "  position: rows:" +keyRows + " cloums:" + keyCloumns);
    }

    public static void main(String[] args) {
        findOneInTwoDimenArray1(4, 5, 8);
    }
}
  • 以下测试案例:
//测试一:正确
inPut:4,5,8
outPut:
1  2  3  4  5  
2  4  6  8  10  
3  6  9  12  15  
4  8  12  16  20  
isInArray: true  position: rows:1 cloums:3
//测试二:错误
input:5,4,8
1  2  3  4  
2  4  6  8  
3  6  9  12  
4  8  12  16  
5  10  15  20  
isInArray: false  position: rows:-1 cloums:-1
  • 如上测试用例可知,以上解法是错误的,原因在于虽然此二维数组每一列每一行都递增,当并不代表中间元素mid之前的元素一定小于mid导致程序出错。
正确解法分析过程如下
  • 还是将二维数组看成矩阵,,从中选一个元素 m与目标值key比较,分三种情况分析查找,

    • 情况一 当 m == key,结束查找
    • 情况二 当 m < key,根据数组排序规则,要查找的数字应该在当前选区的位置的右边或者下面
      在这里插入图片描述
    • 情况三 当m > key,同样的,要查的数字应该在左边或者上面
      在这里插入图片描述
      在这里插入图片描述
  • 如上分析,由于需要朝招的数字相对当前选取的m的位置有可能两个区域,而且有重叠,这样看起来就复杂了。

  • 我们将问题具体化,。下面以上图中矩阵中查找数字7 为案例,一步步分析查找过程。

  • 之前之所以困难,是我们在二维数组的中间选取的数字m 与目标值key比较,这样导致下一次需要查找的两个相互重叠的区域。如果我们从数组的一个角上选取,情况如下:

    • 首先选右上角数字9, 9 > 7,并且9 还在第四列的第一个数字,所有,7 不可能在第四列
      在这里插入图片描述

    • 将剩下三列看成一个新的二维数组,同样选右上角 8 > 7 ,同样可以排除第三列数
      在这里插入图片描述

    • 将剩下两列看成一个新二维数组,同样选右上角2 < 7,那么要查找的7 可能在2的右边也可能在2 的下面,因为之前的操作已经将右边的所有数据都排除了,所有必定在2 的下面,并且将2 这一行踢出,只剩下3*2 的二维数组,如下
      在这里插入图片描述

    • 剩下上图中,同样用这个方法,去右上角4 < 7 ,所以不再这一行,可能在4下面,剩下2*2 的二维数组
      在这里插入图片描述

  • 接着在取右上角 7 = 7 得出最终答案。

总结
  • 以上查找过程,有如下规律:首先选取数组中右上角元素,
    • 如果改数字等于要查找数字,结束查找
    • 如果改数字小于要查找数字,踢出这个数字所在行
    • 如果这个数字大于要查找的数字,踢出这个数字所在的列
    • 依次每一步都在查找范围内踢出一列或者一行,逐步缩小范围。直到查询到或者为空。
    • 如下代码实现。
    • 时间复杂度 O(max(rows, cloumns)),空间复杂度 O(1)
/**
 * 
 * @author liaojiamin
 * @Date:Created in 13:59 2020/10/30
 */
public class FindOne {
    public static Integer[][] getTwoDimenArray(int rows, int cloumns){
        Integer[][] twoDimen = new Integer[rows][cloumns];
        for (int i = 0; i < rows; i++) {
            for (int j = 0; j < cloumns; j++) {
                Integer key = (i+1) * (j+1);
                twoDimen[i][j] = key;
                System.out.print(key + "  ");
            }
            System.out.println("");
        }
        return twoDimen;
    }

    public static void findOneInTwoDimenArray2(int rows, int cloumns, Integer key){
        if(rows <=0 || cloumns <=0 || key == null){
            return;
        }
        Integer[][] twoDimen = getTwoDimenArray(rows, cloumns);
        if(twoDimen == null){
            return;
        }
        boolean isInArray = false;
        int keyCloumns = cloumns -1;
        int keyRows =0;
        while (keyCloumns >= 0 && keyRows < rows){
            if(twoDimen[keyRows][keyCloumns] == key){
                System.out.println("isInArray: "+ isInArray + "  position: rows:" +keyRows + " cloums:" + keyCloumns);
                break;
            }else if(twoDimen[keyRows][keyCloumns] > key){
                keyCloumns --;
            }else {
                keyRows ++;
            }
        }
    }
    
    public static void main(String[] args) {
        findOneInTwoDimenArray2(5, 4, 8);
    }
}
  • 测试用例:
    • 二维数组中查找包含的数字,数组中最大值,最小值,介于最大最小之间的值
    • 二维数组中查找不包含的数字,大于最大值,小于最小值,介于最大最小之前的不存在的值
    • 特殊输入 null
其他思路
  • 以上是去右上角的数字,统一,可以选左下角的数字,
  • 不能选左上角的数字,例如选择1, 1 < 7 ,那么7 位于1 的下面或者右边,没有参考意义。
  • 不能选右下角的数字,例如15 , 15 > 7,那么7 位于15 的上面或者左边,没有参考意义。

上一篇 数据结构与算法–实现Singleton模式
下一篇:数据结构与算法–字符串:字符串替换

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值