【刷题之路Ⅱ】牛客 BM18 二维数组中的查找

一、题目描述

原题连接: BM18 二维数组中的查找

题目描述:
在一个二维数组array中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,
每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。
[
[1,2,8,9],
[2,4,9,12],
[4,7,10,13],
[6,8,11,15]
]
给定 target = 7,返回 true。
给定 target = 3,返回 false。

数据范围:矩阵的长宽满足0≤n,m≤500 , 矩阵中的值满足0≤val≤10^9
进阶:空间复杂度O(1) ,时间复杂度O(n+m)

示例1
输入: 7,[[1,2,8,9],[2,4,9,12],[4,7,10,13],[6,8,11,15]]
返回值: true
说明:存在7,返回true

示例2
输入: 1,[[2]]
返回值: false

示例3
输入: 3,[[1,2,8,9],[2,4,9,12],[4,7,10,13],[6,8,11,15]]
返回值: false
说明:不存在3,返回false

二、解题

1、方法1——直接遍历

1.1、思路分析

直接遍历数组中的每一个元素,找到target就返回true,
否则就返回false。

1.2、代码实现

有了以上思路,那我们写起代码来也就水到渠成了:

bool Find1(int target, int** array, int arrayRowLen, int* arrayColLen) {
    assert(array && arrayColLen);
    // 特殊情况特殊处理
    if (target < array[0][0] || target > array[arrayRowLen - 1][*arrayColLen - 1]) {
        return false;
    }
    int i = 0;
    int j = 0;
    for (i = 0; i < arrayRowLen; i++) {
        for (j = 0; j < *arrayColLen; j++) {
            if (array[i][j] == target) {
                return true;
            }
    }
    }
    return false;
}

时间复杂度:O(mn),其中m为矩阵的行数,n为矩阵的列数。
空间复杂度:O(1),我们只需要用到常数级的额外空间。

2、方法2——锁定范围后遍历

2.1、思路分析

通过题目描述,我们知道在同一层左边的元素一定是小于右边的元素的
而竖着层数的增加,每一列上的元素也是递增的,因此我们就可以先找到target所在的层数,然后再遍历这一层,若找到target,则直接返回true。
否则就再判断下一层。
例如下图的例子:
在这里插入图片描述

2.2、代码实现

有了以上思路,那我们写起代码来也就水到渠成了:

bool Find2(int target, int** array, int arrayRowLen, int* arrayColLen) {
    assert(array && arrayColLen);
    // 特殊情况特殊处理
    if (target < array[0][0] || target > array[arrayRowLen - 1][*arrayColLen - 1]) {
        return false;
    }
    int i = 0;
    int j = 0;
    for (i = 0; i < arrayRowLen; i++) {
        if (target > array[i][0] && target < array[i][*arrayColLen - 1]) {
            for (j = 0; j < *arrayColLen - 1; j++) {
                if (target == array[i][j]) {
                    return true;
                }
            }
        }
        else if (target == array[i][0] || target == array[i][*arrayColLen - 1]) {
            return true;
        }
    }
    return false;
}

时间复杂度:O(mn),其中m为矩阵的行数,n为矩阵的列数,最坏情况下,我们要把数组中的mn - 1 一个元素都遍历一遍。
空间复杂度:O(1),我们只需要用到常数级的额外空间。

2.3、改进

改进思路:
既然题目已经说明是有序的了,那我们找到target可能所在的层数之后,完全可以用二分法在那层查找。
代码实现:
有了以上思路,那我们再写起代码来也就水到渠成了:

bool Find3(int target, int** array, int arrayRowLen, int* arrayColLen) {
    assert(array && arrayColLen);
    // 特殊情况特殊处理
    if (target < array[0][0] || target > array[arrayRowLen - 1][*arrayColLen - 1]) {
        return false;
    }
    int i = 0;
    int j = 0;
    for (i = 0; i < arrayRowLen; i++) {
        if (target > array[i][0] && target < array[i][*arrayColLen - 1]) {
            int left = 0;
            int right = *arrayColLen - 1;
            int mid = 0;
            while (left <= right) {
                mid = left + (right - left) / 2;
                if (target < array[i][mid]) {
                    right = mid - 1;
                }
                else if (target > array[i][mid]) {
                    left = mid + 1;
                }
                else {
                    return true;
                }
            }
        }
        else if (target == array[i][0] || target == array[i][*arrayColLen - 1]) {
            return true;
        }
    }
    return false;
}

时间复杂度:O(mlog2N),其中m为矩阵的行数,N为矩阵的列数,最坏情况下,我们需要在每一行都使用一次二分查找。
空间复杂度:O(1),我们只需要用到常数级的额外空间。

3、方法3——标记值移动法

3.1、思路分析

根据题目的叙述,我们可以事先标记好一个flag值,然后移动这个标值进行查找,具体方法如下:
假设我们选初始的标记值为最靠边的左上角的元素,
1、如果flag == target,直接返回,
2、如果target > flag,说明target在更大的位置,而flag左边的元素显然都是小于flag的,简介就小于target,说明第0行是无效的,
所flag要向下移动到下一行。
3、如果target < flag,说明target在更小的地方,而flag下边的元素又都是大于flag的,间接大于target,说明flag所在的列是无效的,
所以flag要向前移动一列。
4、重复上述过程,直到找到或者找不到。
图解过程如下:
开始,我们的flag在矩阵的右上角:
在这里插入图片描述
但此时flag > target 所以最右列就应该被淘汰,flag向左前进一个数:
在这里插入图片描述此时flag为8,还是大于target,所以我们继续淘汰掉一列:
在这里插入图片描述
此时的flag为2,小于target,所以我们接下来应该淘汰的是第1行:
在这里插入图片描述
这里的flag还是小于target,所以我们在淘汰一行就能顺利地找到target了。

这样做是我们在每次比较的时候,都可以淘汰掉一列或一行,从而大大提高了查找效率。

3.2、代码实现

有了以上思路,那我们写起代码来也就水到渠成了:

bool Find4(int target, int** array, int arrayRowLen, int* arrayColLen) {
    assert(array && arrayColLen);
    // 特殊情况特殊处理
    if (target < array[0][0] || target > array[arrayRowLen - 1][*arrayColLen - 1]) {
        return false;
    }
    int flag = 0;
    int row = 0; // 记录flag所在的行数
    int col = *arrayColLen - 1; // 记录flag所在的列数
    while (row < arrayRowLen && col >= 0) {
        flag = array[row][col];
        if (target < flag) {
            col--;
        }
        else if (target > flag) {
            row++;
        }
        else {
            return true;
        }
    }
    return false;
}

时间复杂度:O(m + n),其中为矩阵的行数,n为矩阵的列数,最坏情况下,我们要把每一行每一列都淘汰掉。
空间复杂度:O(1),我们只需要用到常数级的额外空间。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

林先生-1

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值