LeetCode - 面试题04 - 二维数组中的查找

一 目录

不折腾的前端,和咸鱼有什么区别

目录
一 目录
二 题目
三 解题思路
四 统计分析
五 解题套路

二 题目

在一个 n * m 的二维数组中:
每一行都按照从左到右递增的顺序排序,
每一列都按照从上到下递增的顺序排序。

请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。

示例:

现有矩阵 matrix 如下:

[
  [1,   4,  7, 11, 15],
  [2,   5,  8, 12, 19],
  [3,   6,  9, 16, 22],
  [10, 13, 14, 17, 24],
  [18, 21, 23, 26, 30]
]

给定 target = 5,返回 true。
给定 target = 20,返回 false。

限制:

0 <= n <= 1000
0 <= m <= 1000

注意:本题与主站 240 题相同:
https://leetcode-cn.com/problems/search-a-2d-matrix-ii/

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/er-wei-shu-zu-zhong-de-cha-zhao-lcof
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
/**
 * @param {number[][]} matrix
 * @param {number} target
 * @return {boolean}
 */
var findNumberIn2DArray = function(matrix, target) {

};

根据上面的已知函数,小伙伴们可以先尝试破解本题,确定了自己的答案后再看下面代码。

三 解题思路

拿到题目,惯例分析:

  1. 暴力破解:二次循环遍历,查找到值

  2. 暴力优化:设置中断条件

  3. 字符串查找

  4. 【X】区间判断:先横排,再纵排,二分查找

半个钟后出来的结果不是很乐观,第 4 种方法没有很好地想出来,但是第 4 种应该是最优化的。

最后参考了下题解,优化成:

  1. 暴力破解:二次循环遍历,查找到值

  2. 暴力优化:设置中断条件

  3. 字符串查找

  4. 【X】区间判断:先横排,再纵排,二分查找

  5. 利用题意:每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。参考:https://leetcode-cn.com/problems/er-wei-shu-zu-zhong-de-cha-zhao-lcof/solution/mian-shi-ti-04-er-wei-shu-zu-zhong-de-cha-zhao-zuo/

OK,下面一一讲解我的思路技巧:


1、暴力破解:二次循环遍历,查找到值

var findNumberIn2DArray = function(matrix, target) {
  for (let i = 0; i < matrix.length; i++) {
    for (let j = 0; j < matrix[i].length; j++) {
      if (matrix[i][j] === target) {
        return true;
      }
    }
  }
  return false;
};

这个是最容易想到的了,懂得 for 输出 九九乘法表 的,这个应该都能写出来。


2、暴力优化:设置中断条件

var findNumberIn2DArray = function(matrix, target) {
  for (let i = 0; i < matrix.length; i++) {
    for (let j = 0; j < matrix[i].length; j++) {
      if (matrix[i][j] === target) {
        return true;
      }
      if (matrix[i][j] > target) {
        break;
      }
    }
  }
  return false;
};

在上面的代码中,我们傻乎乎地进行了暴力破解,一行一行查找,那么有没有法子更快呢?

有!当我们遍历每一行的时候,如果该行的其中某个元素大于 target 之后,我们就不需要判断后面的数字了,因为题目表明:每一行是递增的,每一列也是递增的

仔细想了一下,它比不上最优解,是因为它的起手位置不对,详细可以看后面


3、字符串查找

var findNumberIn2DArray = function(matrix, target) {
  let matrixStr = '';
  for (let i = 0; i < matrix.length; i++) {
    matrixStr += `#${matrix[i].join('#')}#`;
  }
  if (matrixStr.includes(`#${target}#`)) {
    return true;
  }
  return false;
};

这种方法应该属于 奇技淫巧 了,怎么说?

因为我将整个二维数组换成一个字符串,然后判断字符串是否包含某元素。

虽然看似只有 for(),其实应该是 for() + join(),跟暴力破解一样的傻憨憨。


4、题解区

var findNumberIn2DArray = function(matrix, target) {
  let i = matrix.length - 1, j = 0;
  while (i >= 0 && j < matrix[0].length) {
    if (matrix[i][j] > target) {
      i -= 1;
    } else if (matrix[i][j] < target) {
      j += 1;
    } else {
      return true;
    }
  }
  return false;
};

这种方法看到后很容易理解,但是如果是自己写的话可能不太容易想到。

这里从表格的左下角开始,逐步向右上角查找:


列1列2列3列4列5
行11471115
行22581219
行33691622
行41013141724
行51821232630

假设我们需要查找 8,从 行5列1 开始:

  1. 行5列1:开头 18 大于 8,排除这一行(i -= 1

  2. 行4列1:开头 10 大于 8,排除这一行(i -= 1

  3. 行3列1:开头 3 小于 8,干掉这一列(j += 1

  4. 行3列2:开头 6 小于 8,干掉这一列(j += 1

  5. 行3列3:开头 9 大于 8,干掉这一行(i -= 1

  6. 行2列3:开头 8,返回 true

这就是这种解法的好处。


这时候,我们回到解法 2 中 jsliang 说的那句话:

  • 仔细想了一下,它比不上最优解,是因为它的起手位置不对,详细可以看后面

为什么会有这句话呢?

我们可以先看一份代码,数据证明优先:

const matrix = [
  [1, 4, 7, 11, 15],
  [2, 5, 8, 12, 19],
  [3, 6, 9, 16, 22],
  [10, 13, 14, 17, 24],
  [18, 21, 23, 26, 30],
];

console.time('暴力优化');
var findNumberIn2DArray1 = function(matrix, target) {
  for (let i = 0; i < matrix.length; i++) {
    for (let j = 0; j < matrix[i].length; j++) {
      if (matrix[i][j] === target) {
        return true;
      }
      if (matrix[i][j] > target) {
        break;
      }
    }
  }
  return false;
};
findNumberIn2DArray1(matrix, 30);
console.timeEnd('暴力优化');

console.time('题解区');
var findNumberIn2DArray2 = function(matrix, target) {
  let i = matrix.length - 1, j = 0;
  while (i >= 0 && j < matrix[0].length) {
    if (matrix[i][j] > target) {
      i -= 1;
    } else if (matrix[i][j] < target) {
      j += 1;
    } else {
      return true;
    }
  }
  return false;
};
findNumberIn2DArray2(matrix, 15);
console.timeEnd('题解区');

输出结果(5 次):

暴力优化: 0.317ms
题解区: 0.138ms

暴力优化: 0.193ms
题解区: 0.070ms

暴力优化: 0.194ms
题解区: 0.088ms

暴力优化: 0.193ms
题解区: 0.069ms

暴力优化: 0.192ms
题解区: 0.070ms

可以看到的的确确是比我们的暴力优化快,原因在哪?

对于表格:


列1列2列3列4列5
行11471115
行22581219
行33691622
行41013141724
行51821232630
  • 暴力优化查找,从左上角到右下角,需要 25 次(整个表)

  • 题解区查找,从左下角到右上角,需要 9 次(18 -> 10 -> 13 -> 14 -> 17 -> 16 -> 12 -> 11 -> 15)

虽然都是中断,但是起手位置决定了你需要走的次数,分析出这次的内容,甘拜下风!

四 统计分析

这四种解法在 LeetCode 的统计如下:

解法执行用时 / 击败率内存消耗 / 击败率
解法 184 ms / 27.85%37.4 MB / 100.00%
解法 272 ms / 70.33%37.4 MB / 100.00%
解法 392 ms / 19.02%43.8 MB / 100.00%
解法 460 ms / 97.63%36.3 MB / 100.00%

其实不看也一样,上面已经分析得明明白白了。

五 套路分析

本题暂未发现任何套路,如果有但是 jsliang 后面发现了的话,会在 GitHub 进行补充。

如果小伙伴有更好的思路想法,或者没看懂其中某种解法,欢迎评论留言或者私聊 jsliang~


不折腾的前端,和咸鱼有什么区别!

jsliang 会每天更新一道 LeetCode 题解,从而帮助小伙伴们夯实原生 JS 基础,了解与学习算法与数据结构。

浪子神剑 会每天更新面试题,以面试题为驱动来带动大家学习,坚持每天学习与思考,每天进步一点!

扫描上方二维码,关注 jsliang 的公众号(左)和 浪子神剑 的公众号(右),让我们一起折腾!

jsliang 的文档库 由 梁峻荣 采用 知识共享 署名-非商业性使用-相同方式共享 4.0 国际 许可协议进行许可。
基于https://github.com/LiangJunrong/document-library上的作品创作。
本许可协议授权之外的使用权限可以从 https://creativecommons.org/licenses/by-nc-sa/2.5/cn/ 处获得。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值