在一个二维数组array中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数

文章描述了一种在二维数组中,每个一维数组按递增顺序排列的查找算法。通过先找到最大值区域,然后利用二分法逐步缩小范围或扩展查找,最终确定目标整数是否存在。
摘要由CSDN通过智能技术生成

题目:

在一个二维数组array中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。

[

[1,2,8,9],
[2,4,9,12],
[4,7,10,13],
[6,8,11,15]

]

给定 target = 7,返回 true。

给定 target = 3,返回 false。

0 <= array.length <= 500
0 <= array[0].length <= 500

思路:

将二维数组看成由多个数字组成的坐标系,已知每一行从左到右递增,每一列从上到下递增,所以左上角的数字是最小的,设为原点,如下图:

在这里插入图片描述

因为每个一维数组的长度相同,所以排列形成的必然是矩形,并且根据排列可知,矩形右下角的数字最大因为从左往右、从上往下递增

在这里插入图片描述

在这里插入图片描述

所以在一个二维数组array中寻找一个数字,只需要先找一个矩形右下角的最大值,如果最大值小于要寻找的数字,那么这个矩形内的数字都可以排除。

那么要先从哪个矩形开始找呢?

因为采用顺序排列,我们可以采用二分法,先从初始矩形二分之一的矩形开始查找最大值。

在这里插入图片描述
如果矩形右下角的值等于要查找的数字,直接完成。
如果矩形右下角的值大于要查找的数字,那么就缩小矩形的范围,用二分法继续查找;
如果矩形右下角的值小于要查找的数字,那么就扩大矩形的范围,用二分法继续查找,同时记录当前矩形右下角坐标为最大值所在坐标;

直到查找完成,如果没有找到目标数字,那么最大值所在坐标与原点所圈出的矩形内的数字肯定都不满足,以此为起点从矩形外围开始查找。

假设要查找13,经过二分法查找后,如下图:

在这里插入图片描述
查到的最大值为10,黄色矩形内数字都不满足,以坐标(2,2)为起点,向外层查找

外层有2个最小值,分别为 9、6,也就是横、纵坐标分别为0的时候。

在这里插入图片描述

如果两个最小值大于要查找的数字,则无需继续查找。
否则的话,分别查找外层所在的横列与纵列,可以继续采用二分查找

在这里插入图片描述
这里由于数字有限,查找1层就找到了,如果外层没有查找到,继续向外扩大1层查找,直到整个二维数组所对应的矩形查找完。

代码:

import java.util.Arrays;

public class Search {

    public static void main(String[] args) {
        int[][] arr = {{1,2,8,9,10,12},{2,4,9,11,13,14},{4,7,10,12,14,15},{6,8,11,15,16,17},{7,9,13,16,18,19}};
        int n = 17;
        for (int[] ints : arr) {
            System.out.println(Arrays.toString(ints));
        }
        System.out.println("---------开始二分查找小于[" + n +"]的最大矩形-----------");
        int[] maxArr = findMax(arr, n);
        if (maxArr[0] == 0) {
            System.out.println("最大矩形右下角坐标为:[" + maxArr[1] + ", " + maxArr[2] + "]");
            System.out.println("---------开始从最大矩形外查找-----------");
            boolean result = find(arr, n, maxArr[1] + 1, maxArr[2] + 1);
            System.out.println(result);
        } else {
            printAB(maxArr[1], maxArr[2]);
            System.out.println(true);
        }
    }

    public static int[] findMax(int[][] arr,int n) {
        int a1 = 0;
        int b1 = 0;
        int b2 = arr.length - 1;
        int a2 = arr[b2].length - 1;
        int[] maxArr = new int[] {0, 0, 0};
        while (b1 <= b2 && a1 <= a2) {
            int x = (a1 + a2) / 2;
            int y = (b1 + b2) / 2;
            if (arr[y][x] == n) {
                return new int[] {1, x, y};
            }
            if (arr[y][x] > n) {
                a2 = x - 1;
                b2 = y - 1;
            }
            if (arr[y][x] < n) {
                a1 = x + 1;
                b1 = y + 1;
                maxArr[1] = x;
                maxArr[2] = y;
            }
        }

        return maxArr;
    }

    public static boolean find(int[][] arr,int n, int a, int b) {
        boolean goonA = true;
        boolean goonB = true;
        while (goonA || goonB) {
            if (arr.length == b) {
                b--;
            }
            if (arr[b].length == a) {
                a--;
            }
            System.out.println("---------开始查找坐标为[" + a + ", " + b + "]范围内的数据---------");
            if (arr[b][a] == n) {
                printAB(a,b);
                return true;
            }
            if (arr[b][0] > n && arr[0][a] > n) {
                return false;
            }
            if (arr[b][a] > n) {
                if (goonA) {
                    if (find(arr, a, b, n, true)) {
                        return true;
                    }
                }
                if (goonB) {
                    if (find(arr, a, b, n, false)) {
                        return true;
                    }
                }
            }
            a++;
            b++;
            if (arr.length == b) {
                goonB = false;
            }
            if (arr[b - 1].length == a) {
                goonA = false;
            }
        }
        return false;
    }

    public static boolean find(int[][] arr, int a, int b, int n, boolean isA){
        int start = 0;
        int end = b;
        if (isA)
            end = a;

        if (isA)
            System.out.println("开始二分查找坐标为[0, " + b + "]至[" + a + ", " + b +"]的横列数据...");
        else
            System.out.println("开始二分查找坐标为[" + a +", 0]至[" + a + ", " + b +"]的纵列数据...");

        while (start <= end) {
            int mid = (start + end) / 2;
            if (isA)
                a = mid;
            else
                b = mid;
            if (arr[b][a] == n) {
                printAB(a,b);
                return true;
            }
            if (arr[b][a] < n) {
                start = mid + 1;
            }
            if (arr[b][a] > n) {
                end = mid -1;
            }
        }
        return false;
    }

    public static void printAB(int a, int b) {
        System.out.println("---------目标数字已找到!---------");
        System.out.println("目标数字坐标:[" + a + ", " + b + "]");
    }

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值