一 目录
不折腾的前端,和咸鱼有什么区别
目录
一 目录
二 题目
三 解题思路
四 统计分析
五 解题套路
二 题目
在一个 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 <= 10000 <= 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) {};
根据上面的已知函数,小伙伴们可以先尝试破解本题,确定了自己的答案后再看下面代码。
三 解题思路
拿到题目,惯例分析:
暴力破解:二次循环遍历,查找到值
暴力优化:设置中断条件
字符串查找
【X】区间判断:先横排,再纵排,二分查找
半个钟后出来的结果不是很乐观,第 4 种方法没有很好地想出来,但是第 4 种应该是最优化的。
最后参考了下题解,优化成:
暴力破解:二次循环遍历,查找到值
暴力优化:设置中断条件
字符串查找
【X】区间判断:先横排,再纵排,二分查找
利用题意:每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。参考: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
行1
1
4
7
11
15
行2
2
5
8
12
19
行3
3
6
9
16
22
行4
10
13
14
17
24
行5
18
21
23
26
30
假设我们需要查找 8,从 行5列1 开始:
行5列1:开头 18 大于 8,排除这一行(
i -= 1
)行4列1:开头 10 大于 8,排除这一行(
i -= 1
)行3列1:开头 3 小于 8,干掉这一列(
j += 1
)行3列2:开头 6 小于 8,干掉这一列(
j += 1
)行3列3:开头 9 大于 8,干掉这一行(
i -= 1
)行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
行1
1
4
7
11
15
行2
2
5
8
12
19
行3
3
6
9
16
22
行4
10
13
14
17
24
行5
18
21
23
26
30
暴力优化查找,从左上角到右下角,需要 25 次(整个表)
题解区查找,从左下角到右上角,需要 9 次(18 -> 10 -> 13 -> 14 -> 17 -> 16 -> 12 -> 11 -> 15)
虽然都是中断,但是起手位置决定了你需要走的次数,分析出这次的内容,甘拜下风!
四 统计分析
这四种解法在 LeetCode 的统计如下:
解法
执行用时 / 击败率
内存消耗 / 击败率
解法 1
84 ms / 27.85%
37.4 MB / 100.00%
解法 2
72 ms / 70.33%
37.4 MB / 100.00%
解法 3
92 ms / 19.02%
43.8 MB / 100.00%
解法 4
60 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/ 处获得。
本文分享自微信公众号 - 飘飞的心灵(gh_e57c56f669b2)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。