[每日一题] 140. 搜索二维矩阵(数学、二分法、多方法)

1. 题目来源

链接:搜索二维矩阵
来源:LeetCode

2. 题目说明

编写一个高效的算法来判断 m x n 矩阵中,是否存在一个目标值。该矩阵具有如下特性:

  • 每行中的整数从左到右按升序排列。
  • 每行的第一个整数大于前一行的最后一个整数。

示例 1:

输入:
matrix = [
[1, 3, 5, 7],
[10, 11, 16, 20],
[23, 30, 34, 50]
]
target = 3
输出: true

示例 2:

输入:
matrix = [
[1, 3, 5, 7],
[10, 11, 16, 20],
[23, 30, 34, 50]
]
target = 13
输出: false

3. 题目解析

3.1 方法一:暴搜

这是个没有营养的解答,会浪费你 10 秒钟~~~

// 执行用时 :12 ms, 在所有 C++ 提交中击败了53.83%的用户
// 内存消耗 :10.1 MB, 在所有 C++ 提交中击败了5.29%的用户

class Solution {
public:
    bool searchMatrix(vector<vector<int>>& matrix, int target) {
        if (matrix.empty() || matrix[0].empty())
            return false;
        for (int i = 0; i < matrix.size(); ++i) {
            for (int j = 0; j < matrix[0].size(); ++j) {
                if (target == matrix[i][j])
                    return true;
            }
        }
        return false;
    }
};

3.2 方法二:朴素二分法、细节处理

题目很明确,矩阵元素有序,就是思考如何二分,思路如下:

  • 如果是查找第一个不小于目标值的数,当 target 在第一列时,会返回 target 所在的行,但若 target 不在的话,有可能会返回下一行,不好统一
  • 故可以查找第一个大于目标值的数,这样只要回退一个,就一定是 target 所在的行。但需要注意的一点是,如果返回的是 0,就不能回退了,以免越界,记得要判断一下
  • 找到了 target 所在的行数,就使用二分法即可

参见代码如下:

// 执行用时 :12 ms, 在所有 C++ 提交中击败了53.83%的用户
// 内存消耗 :9.8 MB, 在所有 C++ 提交中击败了22.75%的用户

class Solution {
public:
    bool searchMatrix(vector<vector<int>>& matrix, int target) {
        if (matrix.empty() || matrix[0].empty()) 
            return false;
        int left = 0, right = matrix.size();
        while (left < right) {
            int mid = (left + right) / 2;
            if (matrix[mid][0] == target) 
                return true;
            if (matrix[mid][0] <= target) 
            left = mid + 1;
            else 
                right = mid;
        }
        int tmp = (right > 0) ? (right - 1) : right;
        left = 0;
        right = matrix[tmp].size();
        while (left < right) {
            int mid = (left + right) / 2;
            if (matrix[tmp][mid] == target)     
                return true;
            if (matrix[tmp][mid] < target) 
                left = mid + 1;
            else 
                right = mid;
        }
        return false;
    }
};

3.2 方法二:依据坐标关系二分法

这道题也可以使用一次二分查找法:

  • 按S型遍历该二维数组,可以得到一个有序的一维数组,只需要用一次二分查找法
  • 关键在于坐标的转换,如何把二维坐标和一维坐标转换是关键点:
  • 把一个长度为n的一维数组转化为 m * n 的二维数组 (m * n = n)后,那么原一维数组中下标为 i 的元素将出现在二维数组中的 [i / n][i % n] 的位置,有了这一点

参见代码如下:

// 执行用时 :4 ms, 在所有 C++ 提交中击败了99.52%的用户
// 内存消耗 :10 MB, 在所有 C++ 提交中击败了5.29%的用户

class Solution {
public:
    bool searchMatrix(vector<vector<int>>& matrix, int target) {
        if (matrix.empty() || matrix[0].empty()) 
            return false;
        int m = matrix.size(), n = matrix[0].size();
        int left = 0, right = m * n;
        while (left < right) {
            int mid = (left + right) / 2;
            if (matrix[mid / n][mid % n] == target) 
                return true;
            if (matrix[mid / n][mid % n] < target) 
                left = mid + 1;
            else 
                right = mid;
        }
        return false;
    }
};

3.3 双指针法

这道题其实也可以不用二分搜索法,直接使用双指针也是可以的:

  • i 指向 0,j 指向列数,这样第一个被验证的数就是二维数组右上角的数字
  • 若等于 target,直接返回 true
  • 若大于 target,说明要减小数字,则列数 j 自减 1
  • 若小于 target,说明要增加数字,行数 i 自增 1
  • while 循环退出了还是没找到 target,直接返回 false 即可

参见代码如下:

// 执行用时 :0 ms, 在所有 C++ 提交中击败了100.00%的用户
// 内存消耗 :10 MB, 在所有 C++ 提交中击败了5.29%的用户
class Solution {
public:
    bool searchMatrix(vector<vector<int>>& matrix, int target) {
        if (matrix.empty() || matrix[0].empty())   
            return false;
        int i = 0, j = (int)matrix[0].size() - 1;
        while (i < matrix.size() && j >= 0) {
            if (matrix[i][j] == target) 
                return true;
            else if 
                (matrix[i][j] > target) --j;
            else 
                ++i;
        }   
        return false;
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Ypuyu

如果帮助到你,可以请作者喝水~

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

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

打赏作者

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

抵扣说明:

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

余额充值