壹 ❀ 引

前些日子,在与博客园用户MrSmileZhu闲聊中,我问到了他先前在字节跳动面试中遇到了哪些算法题(又戳到了他的伤心处),因为当时面试的高度紧张,原题描述已经无法重现了,但大概与数组合并、求交集相关。比较巧的是我在今年年初有整理过一份数组常用操作的文章的JS 数组常见操作汇总,结果今天leetcode的每日打卡,也正好是求数组交集,此题一共有两题,我分别试了下,也看了其他用户思路,才发现原来花样有这么多,所以这篇文章是关于这两题的思路汇总。不积跬步,无以至千里;不积小流,无以成江海。那么本文开始。

贰 ❀ 两个数组的交集

此题来自leetcode349. 两个数组的交集,题目描述如下:

给定两个数组,编写一个函数来计算它们的交集。

示例 1:

输入:nums1 = [1,2,2,1], nums2 = [2,2]
输出:[2]

示例 2:

输入:nums1 = [4,9,5], nums2 = [9,4,9,8,4]
输出:[9,4]

说明:

输出结果中的每个元素一定是唯一的。

我们可以不考虑输出结果的顺序。

注意,在例子1中,随便两个数组都有两个2,但是认定交集只有一个2;结合说明中输出结果每个元素都是唯一,我们可知题目要求无非是两点。

  1. 这个元素一定是在nums1中与nums2中都有出现
  2. 不管出现几次,只要是同一个元素都认定为是一个元素。

贰 ❀ 壹 借用哈希表

所以我的想法是,以其中一个数组为遍历参照物,依次查找另一个数组中有没有当前元素,考虑要求2,所以我还需要一个额外哈希表,记录已经出现过的元素,那么直接上代码:

/**
* @param {number[]} nums1
* @param {number[]} nums2
* @return {number[]}
*/
var intersection = function (nums1, nums2) {
// 定义一个哈希表,记录出现过的元素
let dic = {},
ans = [];
for (let i = 0, len = nums1.length; i < len; i++) {
// 这个元素在两边数组都有出现,其次它还得是第一次向淮安
if (nums2.includes(nums1[i]) && !dic[nums1[i]]) {
// 记录出现过的每个元素
dic[nums1[i]] = true;
ans.push(nums1[i]);
};
};
return ans;
};

思路很简单,只是额外加了一个哈希表用于缓存出现过的元素,比如示例1中的2,不管你出现几次,我只有将你第一次出现时加入进去。

贰 ❀ 贰 借用set元素独一特性

其实针对第一个例子,如果我们不用hash表保证元素第一次出现,就会出现元素满足几次就记录几次的问题,比如下面的代码:

var intersection = function (nums1, nums2) {
return nums1.filter(item => nums2.includes(item)));
};

这个实现用于测试示例1,就会得到[2,2]。灵机一动,我们也可以在得到结果之后再加工啊,比如set结构不接受重复元素,所以有了如下实现:

/**
* @param {number[]} nums1
* @param {number[]} nums2
* @return {number[]}
*/
var intersection = function (nums1, nums2) {
return [...new Set(nums1.filter(item => nums2.includes(item)))];
};

我能想到的也就这两点了,其它的思路均来自leetcode用户秦时明月

贰 ❀ 叁 借用set结构

由于我们只是要找两个数组中都有的元素,且只记录第一次,我们完全可以用new Set过滤掉重复元素。

/**
* @param {number[]} nums1
* @param {number[]} nums2
* @return {number[]}
*/
var intersection = function (nums1, nums2) {
// 保证nums1是长度更小那个
if (nums1.length > nums2.length) {
[nums1, nums2] = [nums2, nums1];
};
let hash = new Set(nums1);
let res = new Set();
for (let i = 0; i < nums2.length; i++) {
// 注意,由于是set结构,这里用has取代了数组的includes
if (hash.has(nums2[i])) {
res.add(nums2[i]);
};
};
return [...res];
};

然而我看到这个思路,是这么想的:

/**
* @param {number[]} nums1
* @param {number[]} nums2
* @return {number[]}
*/
var intersection = function (nums1, nums2) {
if (nums1.length > nums2.length) {
[nums1, nums2] = [nums2, nums1]
};
let hash1 = new Set(nums1);
let hash2 = new Set(nums2);
let res = new Set();
for (let num of hash1) {
if (hash2.has(num)) {
res.add(num);
};
};
return [...res];
};

我在上篇文章中就提出了一个疑问,数组的includes和set的has查找,到底谁更快,在知乎不精确测试中提到,当数据小于一万,has更快,大于一万时,includes更具优势,闲的无聊,我也做了一个测试,但事实证明不管数据如何,has查找似乎都比includes更具优势:

// 创建一个0-99999的数组
let arr = Array.from(Array(100000), (v, k) => k);
let set = new Set(arr);
// 数组测试,执行100次
console.time('arr');
for (let i = 0; i < 100; i++) {
arr.includes(10000);
};
console.timeEnd('arr');
// set测试,执行100次
console.time('set');
for (let i = 0; i < 100; i++) {
set.has(10000);
};
console.timeEnd('set');

大家可以修改数组长度与执行次数,以及需要查找的数,我多次刷新让大家看看时间对比

由于以上例子数组范围是[0,99999],如果我们将查找的数改为100000,由于不存在这个数,也就是每次查找都会从头找到尾,时间差异就更大了。

当然还有二分查找等其它思路,这里我不做一一记录,可点击上方秦时明月查看,关于本题先说到这里。

叁 ❀ 两个数组的交集 II

来看看题二,题二来源leetcode350. 两个数组的交集 II,题目描述如下:

给定两个数组,编写一个函数来计算它们的交集。

示例 1:

输入:nums1 = [1,2,2,1], nums2 = [2,2]
输出:[2,2]

示例 2:

输入:nums1 = [4,9,5], nums2 = [9,4,9,8,4]
输出:[4,9]

说明:

输出结果中每个元素出现的次数,应与元素在两个数组中出现次数的最小值一致。

我们可以不考虑输出结果的顺序。

进阶:

如果给定的数组已经排好序呢?你将如何优化你的算法?

如果 nums1 的大小比 nums2 小很多,哪种方法更优?

如果 nums2 的元素存储在磁盘上,磁盘内存是有限的,并且你不能一次加载所有的元素到内存中,你该怎么办?

与题一不同的是,题二要求,如果两个数组同时存在相同元素,即便元素已重复,仍认为是交集,比如实例1中双方都有两个2,所以输出了[2,2]

叁 ❀ 壹 我的思路,开心爱消除

按照常规的思路[2,2][2]由于2次对比都满足,会输出[2,2],而本题必须要数量上还对等,所以我将其理解成开心爱消除,找到一个消除一个,直接上代码:

/**
* @param {number[]} nums1
* @param {number[]} nums2
* @return {number[]}
*/
var intersect = function (nums1, nums2) {
let ans = [];
nums1.forEach((item, index) => {
let sub = nums2.indexOf(item);
if (sub > -1) {
ans.push(item);
// 找到1个删掉一个
nums2.splice(sub, 1);
}
});
return ans;
};

叁 ❀ 其它优秀思路

我们在题一中利用哈希证明元素是第一次出现,在这里,同理可以借用哈希,记录每个元素出现的次数,满足一次,让次数减一,其实还是同一个道理,这里借用leetcode用户天使爆破组思路:

/**
* @param {number[]} nums1
* @param {number[]} nums2
* @return {number[]}
*/
var intersect = function (nums1, nums2) {
let ans = [];
let hash = {};
// 记录每个元素出现次数
for (let num of nums1) {
hash[num] ? ++hash[num] : hash[num] = 1;
};
// 遍历nums2看看有没有数字在nums1出现过
for (let num of nums2) {
let val = hash[num];
if (val) {
ans.push(num); // 推入res数组
--hash[num]; // 匹配掉一个,就少了一个
};
};
return ans;
};

题目在进阶中,指出如果两数组已排好序如何优化,假设数组已排序,我们大可使用双指针来解决这个问题,大致图示为:

直接上代码:

/**
* @param {number[]} nums1
* @param {number[]} nums2
* @return {number[]}
*/
var intersect = function (nums1, nums2) {
// 先排序,才好使用双指针
nums1.sort((a, b) => a - b);
nums2.sort((a, b) => a - b);
const ans = [];
let p1 = 0;
let p2 = 0;
while (p1 < nums1.length && p2 < nums2.length) {
if (nums1[p1] > nums2[p2]) {
p2++;
} else if (nums1[p1] < nums2[p2]) {
p1++;
} else {
ans.push(nums1[p1]);
p1++;
p2++;
};
};
return ans;
};

那么到这里,本文结束。

JS leetcode 两个数组的交集I II 合集题解分析的更多相关文章

  1. JS - 计算两个数组的交集、差集、并集、补集(多种实现方式)

    方法一:最普遍的做法 使用 ES5 语法来实现虽然会麻烦些,但兼容性最好,不用考虑浏览器 JavaScript 版本.也不用引入其他第三方库. 1,直接使用 filter.concat 来计算 var ...

  2. js取两个数组的交集|差集|并集|补集|去重示例代码

    http://www.jb51.net/article/40385.htm 代码如下: /** * each是一个集合迭代函数,它接受一个函数作为参数和一组可选的参数 * 这个迭代函数依次将集合的每一 ...

  3. leetcode 两个数组的交集 II

    给定两个数组,写一个方法来计算它们的交集. 例如: 给定 nums1 = [1, 2, 2, 1], nums2 = [2, 2], 返回 [2, 2]. /** * @param {number[] ...

  4. js 获取两个数组的交集,并集,补集,差集

    https://blog.csdn.net/piaojiancong/article/details/98199541 ES5 const arr1 = [1,2,3,4,5], arr2 = [5, ...

  5. js求两个数组的交集|并集|差集|去重

    let a = [1,2,3], b= [2, 4, 5]; 1.差集 (a-b 差集:属于a但不属于b的集合)  a-b = [1,3] (b-a 差集:属于b但不属于a的集合)  b-a = [4 ...

  6. 【LeetCode题解】350_两个数组的交集Ⅱ

    目录 [LeetCode题解]350_两个数组的交集Ⅱ 描述 方法一:映射 Java 实现 Python 实现 类似的 Python 实现 方法二:双指针 Java 实现 Python 实现 [Lee ...

  7. 【LeetCode题解】349_两个数组的交集

    目录 [LeetCode题解]349_两个数组的交集 描述 方法一:两个哈希表 Java 实现 类似的 Java 实现 Python 实现 类似的 Python 实现 方法二:双指针 Java 实现 ...

  8. 【Leetcode】【简单】【350. 两个数组的交集 II】【JavaScript】

    题目描述 350. 两个数组的交集 II 给定两个数组,编写一个函数来计算它们的交集. 示例 1: 输入: nums1 = [1,2,2,1], nums2 = [2,2]输出: [2,2] 示例 2 ...

  9. 前端与算法 leetcode 350. 两个数组的交集 II

    目录 # 前端与算法 leetcode 350. 两个数组的交集 II 题目描述 概要 提示 解析 解法一:哈希表 解法二:双指针 解法三:暴力法 算法 # 前端与算法 leetcode 350. 两 ...

  10. Java实现 LeetCode 350 两个数组的交集 II(二)

    350. 两个数组的交集 II 给定两个数组,编写一个函数来计算它们的交集. 示例 1: 输入: nums1 = [1,2,2,1], nums2 = [2,2] 输出: [2,2] 示例 2: 输入 ...

随机推荐

  1. longjmp 使 C++ RAII 失效

    C 语言的 longjmp 没有进行栈展开,而是直接跳转.从 longjmp 到 setjmp 之间的所有析构函数都没有调用,也就是 RAII 失效. #include <setjmp.h> ...

  2. spring-transaction源码分析(5)TransactionInterceptor事务拦截逻辑

    spring-tx的事务拦截逻辑在TransactionInterceptor类,本文将详细分析其实现方式. 事务拦截器TransactionInterceptor spring-tx的事务拦截逻辑在 ...

  3. Go-包-package-modules-import

  4. SV 并发线程

    内容 assign d = a & b; assign e = b | c; begin...end之间的语句是串行执行的 fork....join语句是并行执行的 逻辑仿真工具中的并发性 仿 ...

  5. [转帖]Mysql 常用命令行,持续补充

    https://www.cnblogs.com/wzj1223/p/13152446.html 1.常用命令行 # 登录Mysql mysql -uroot -proot # 查看所有数据库 show ...

  6. [转帖]编译安装goofys挂载Scaleway免费75G对象存储

    日常•2022年5月29日   goofys编译 goofys是一个开源的使用Go编写的s3存储桶挂载工具,主打高性能.由于使用Go编写,没有用到什么特别的依赖,自己编译也很容易.截止2022.5.2 ...

  7. [转帖]chrome历史版本及重大变化(维基百科)

    Google Chrome是Google LLC开发的免费 网络浏览器.开发过程分为不同的"发布渠道",每个发布渠道都在单独的开发阶段进行构建.Chrome提供了4种渠道:稳定版, ...

  8. [转帖]CPU Turbo&Cstate&Pstate简介

    https://www.jianshu.com/p/eaefd1eb0ac6 测试环境 CPU 1 Intel(R) Xeon(R) Gold 5218 CPU @ 2.30GHz 16 3130 3 ...

  9. [转帖]在龙芯3A5000上测试SPEC CPU 2006

    https://baijiahao.baidu.com/s?id=1707601012673143593&wfr=spider&for=pc 注:百家号中,一些文本.代码等的排版格式无 ...

  10. 如何写RN样式 如何写RN组件 如何满屏 如何使用变量

    app.js 文本水平居中了哈 控制文本的大小 字体颜色等 只有在文本元素上去控制哈 import React from 'react'; import {View, Text, StyleSheet ...