349. 两个数组的交集

349. 两个数组的交集

力扣题目链接(opens new window)

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

349. 两个数组的交集

说明: 输出结果中的每个元素一定是唯一的。 我们可以不考虑输出结果的顺序。

思路

这道题目,主要要学会使用一种哈希数据结构:unordered_set,这个数据结构可以解决很多类似的问题。

注意题目特意说明:输出结果中的每个元素一定是唯一的,也就是说输出的结果的去重的, 同时可以不考虑输出结果的顺序

这道题用暴力的解法时间复杂度是O(n^2),那来看看使用哈希法进一步优化。

那么用数组来做哈希表也是不错的选择,例如242. 有效的字母异位词(opens new window)

但是要注意,使用数组来做哈希的题目,是因为题目都限制了数值的大小。

而这道题目没有限制数值的大小,就无法使用数组来做哈希表了。

而且如果哈希值比较少、特别分散、跨度非常大,使用数组就造成空间的极大浪费。

此时就要使用另一种数据结构了,Set。关于 Set,Java 提供了多种可用的实现类,例如:

  • HashSet
  • LinkedHashSet
  • TreeSet

其中,TreeSet 的底层实现是红黑树(基于 NavigableMap,通常是 Red-Black tree),而 HashSetLinkedHashSet 的底层实现是哈希表(HashMap)。

使用 HashSet 读写效率是最高的,它不需要对数据进行排序,并且天然保证元素的唯一性(不允许重复)。因此,在不需要维护元素顺序且追求最高性能的场景下,选择 HashSet 是最合适的。

补充说明:

  • HashSet: 基于哈希表实现,提供最佳的平均时间复杂度(O(1))进行添加、删除和查找操作。不保证元素的迭代顺序。
  • LinkedHashSet: 继承自 HashSet,在哈希表的基础上增加了一个双向链表来维护插入顺序。因此,它保证了元素的迭代顺序与插入顺序一致,同时保持了接近 HashSet 的性能(O(1) 平均时间)。
  • TreeSet: 基于红黑树(一种自平衡二叉查找树)实现。它保证了元素按照其自然顺序(或提供的 Comparator)进行排序。添加、删除和查找操作的时间复杂度为 O(log n),比哈希表实现慢,但能提供有序的集合视图。

思路如c++图所示:

set哈希法

暴力:

// 定义一个名为 Solution 的类
class Solution {
    // 声明一个公共的实例方法 intersection,接收两个整数数组 nums1 和 nums2 作为参数,返回一个整数数组
    // 该方法旨在找出两个数组的交集(元素出现在两个数组中)
    public int[] intersection(int[] nums1, int[] nums2) {
        // 创建一个长度为 1001 的整型数组 hash1,用于记录 nums1 中每个数字出现的次数
        // 索引代表数字本身(0 到 1000),值代表该数字在 nums1 中出现的次数
        int[] hash1 = new int[1001];
        
        // 创建一个长度为 1001 的整型数组 hash2,用于记录 nums2 中每个数字出现的次数
        // 索引代表数字本身(0 到 1000),值代表该数字在 nums2 中出现的次数
        int[] hash2 = new int[1001];
        
        // 使用增强 for 循环遍历 nums1 数组中的每一个元素
        for(int i :nums1){
            // 将当前元素 i 作为索引,将 hash1 数组中对应位置的计数加 1
            // 这样就统计了 nums1 中每个数字的出现频次
            hash1[i]++;
        }
        
        // 使用增强 for 循环遍历 nums2 数组中的每一个元素
        for(int i :nums2){
            // 将当前元素 i 作为索引,将 hash2 数组中对应位置的计数加 1
            // 这样就统计了 nums2 中每个数字的出现频次
            hash2[i]++;
        }
        
        // 创建一个 ArrayList 来动态存储交集元素
        // 使用 List<Integer> 是因为我们事先不知道交集元素的个数
        List<Integer> resList = new ArrayList<>();
        
        // 使用普通 for 循环遍历 hash1 和 hash2 数组的索引(即数字 0 到 1000)
        for(int i=0;i<1001;i++){
            // 检查当前索引 i(代表数字 i)是否同时在 nums1 和 nums2 中出现过
            // hash1[i] > 0 表示数字 i 在 nums1 中至少出现一次
            // hash2[i] > 0 表示数字 i 在 nums2 中至少出现一次
            if(hash1[i]>0 && hash2[i]>0){
                // 如果数字 i 同时存在于两个数组中,则将其添加到结果列表 resList 中
                // 注意:这里只添加一次,即使一个数字在原数组中出现多次,交集也只包含一个
                resList.add(i);
            }
        }
        
        // 初始化一个索引变量 index,用于追踪结果数组 res 中下一个要填充的位置
        int index = 0;
        
        // 创建一个长度等于结果列表 resList 大小的整型数组 res,用于存储最终的交集结果
        int res[] = new int[resList.size()];
        
        // 使用增强 for 循环遍历结果列表 resList 中的每一个元素
        for(int i : resList){
            // 将当前元素 i 赋值给结果数组 res 的 index 位置
            res[index++]=i;
            // 索引 index 自增 1,指向数组中的下一个位置
        }
        
        // 返回最终的交集数组 res
        return res;
    }
    // 方法结束
}
// 类结束
  • 时间复杂度:O(m + n + 1001) ≈ O(m + n),其中 m 和 n 分别是 nums1 和 nums2 的长度。遍历两个数组和长度为 1001 的哈希数组。
  • 空间复杂度:O(1001 + k) ≈ O(1),其中 k 是交集元素的个数。两个固定大小的哈希数组和一个存储结果的动态列表。由于哈希数组大小是固定的 (1001),通常认为是常数空间,但严格来说与输入范围有关。

使用hash:

// 定义一个名为 Solution 的类
class Solution {
    // 声明一个公共的实例方法 intersection,接收两个整数数组 nums1 和 nums2 作为参数,返回一个整数数组
    // 该方法旨在找出两个数组的交集(元素出现在两个数组中)
    public int[] intersection(int[] nums1, int[] nums2) {
        // 检查输入的两个数组是否为 null 或者长度为 0(空数组)
        // 如果满足任一条件,说明无法计算交集或交集为空
        if(nums1 ==null || nums1.length == 0 || nums2 ==null || nums2.length == 0 ){
            // 直接返回一个长度为 0 的新数组,表示空的交集
            return new int[0];
        }
        
        // 创建一个 HashSet,命名为 set,用于存储 nums1 数组中的所有**唯一**元素
        // HashSet 特性:自动去重,查找元素的时间复杂度平均为 O(1)
        Set<Integer> set = new HashSet<>();
        
        // 创建另一个 HashSet,命名为 resSet,用于存储最终的交集元素
        // 同样利用 HashSet 的去重特性,确保结果中没有重复元素
        Set<Integer> resSet = new HashSet<>();
        
        // 使用增强 for 循环遍历 nums1 数组中的每一个元素
        for(int i : nums1){
            // 将当前元素 i 添加到 set 集合中
            // 如果 i 已经存在,add 方法会忽略(保证唯一性)
            set.add(i);
        }
        
        // 使用增强 for 循环遍历 nums2 数组中的每一个元素
        for(int i : nums2){
            // 检查当前元素 i 是否存在于 set 集合中(即是否在 nums1 中出现过)
            // set.contains(i) 利用哈希表查找,效率很高
            if(set.contains(i)){
                // 如果 i 同时存在于 nums1 (通过 set 判断) 和 nums2 中
                // 则将其添加到结果集合 resSet 中
                // HashSet 会自动处理重复添加的情况
                resSet.add(i);
            }
        }
        
        // 创建一个整型数组 res,其长度等于结果集合 resSet 的大小
        // 这样可以精确地分配所需空间,避免浪费
        int[] res = new int[resSet.size()];
        
        // 初始化一个索引变量 index,用于追踪结果数组 res 中下一个要填充的位置
        int index = 0;
        
        // 使用增强 for 循环遍历结果集合 resSet 中的每一个元素
        for(int i : resSet){
            // 将当前元素 i 赋值给结果数组 res 的 index 位置
            // 然后将 index 的值增加 1 (index++ 是后缀自增操作)
            res[index++] = i;
        }
        
        // 返回最终的交集数组 res
        return res;
    }
    // 方法结束
}
// 类结束
  • 时间复杂度:O(m + n),其中 m 和 n 分别是 nums1 和 nums2 的长度。遍历 nums1 构建 set (O(m)),遍历 nums2 并检查 set (O(n),每次 contains 平均 O(1)),最后遍历 resSet 构建结果数组 (O(k),k 是交集大小,k <= min(m, n))。总体是 O(m + n)。
  • 空间复杂度:O(m + k),其中 m 是 nums1 中唯一元素的个数(存储在 set 中),k 是交集元素的个数(存储在 resSet 和最终数组 res 中)。set 的空间是主要开销。
<think>好的,我现在需要帮助用户找到如何使用Lodash.js获取两个数组交集的例子。首先,我得回想一下Lodash库的相关方法。记得Lodash有一个名为_.intersection的函数,可以处理数组交集问题。但用户的具体需求可能是什么?他们可能想要基本用法,或者需要处理更复杂的情况,比如元素重复的情况。 接下来,我需要确认用户提供的引用内容。用户引用了几个LeetCode的问题和解决方案,比如349和350题,这两个题目分别处理数组交集的不同情况。349题要求唯一元素,而350题允许元素出现多次。但用户现在的问题是针对Lodash.js的,所以可能需要将LeetCode中的思路转换为Lodash的实现方式。 Lodash的_.intersection函数默认返回两个数组交集,但每个元素只出现一次,类似于LeetCode的349题的结果。如果用户需要像350题那样保留重复元素,可能需要其他方法,比如计数每个元素出现的次数,并取最小值。但Lodash本身是否有这样的函数呢?或者需要结合其他方法来实现? 用户可能希望得到一个明确的例子,所以需要分步骤说明。首先,引入Lodash库,然后使用_.intersection函数。例如,数组arr1和arr2的交集就是_.intersection(arr1, arr2)。但要注意,结果中的元素是唯一的,不管原数组有多少重复。如果用户需要保留重复次数,可能需要自己处理,比如遍历交集元素,统计在两个数组中出现的次数,然后添加相应的数量。 另外,用户可能遇到的常见问题包括:如何处理多个数组交集?Lodash的_.intersection可以接受多个数组参数,返回所有数组交集。例如,三个数组交集就是_.intersection(arr1, arr2, arr3)。 还需要注意,Lodash的安装和引入方式。用户可能需要知道如何通过npm安装,或者在浏览器中引入CDN链接。例如,使用script标签引入Lodash库,或者在Node.js项目中通过require引入。 然后,我需要检查用户提供的引用内容中的代码示例,看看是否有可以借鉴的地方。例如,引用3和4中的C++代码使用了集合和哈希表来记录元素,以确保唯一性。而引用5中的Python代码使用了集合交集,然后计算每个元素的最小出现次数。如果用户需要类似LeetCode 350题的解决方案,可能需要结合Lodash的_.intersection和其他方法,比如过滤数组,统计次数。 但根据用户的问题,他们可能只需要基本的交集示例,所以首先展示_.intersection的基本用法,然后如果有更复杂的需求,再进一步说明如何处理。比如,如果需要保留重复元素,可以给出一个自定义函数的例子,使用Lodash的计数函数和循环来生成结果数组。 总结,回答应包括以下几点: 1. 引入Lodash库的方法。 2. _.intersection的基本使用示例。 3. 说明结果元素的唯一性。 4. 如何处理多个数组交集。 5. 如果需要保留重复元素,提供自定义解决方案的思路或代码。 同时,要确保回答结构清晰,步骤明确,并引用用户提供的参考资料中的相关信息。例如,引用[^5]中的方法使用了集合交集和最小次数,这可能对用户实现类似功能有帮助,但需要使用Lodash的函数来实现。</think>要使用 Lodash.js 获取两个数组交集,可以通过 `_.intersection` 方法实现。以下是分步说明和示例: --- ### 1. 基本用法 `_.intersection` 方法返回两个数组交集,且结果中的元素**唯一**(类似LeetCode 349题要求[^4]): ```javascript const _ = require('lodash'); // 引入Lodash const arr1 = [1, 2, 2, 3]; const arr2 = [2, 3, 4]; const result = _.intersection(arr1, arr2); console.log(result); // 输出: [2, 3] ``` --- ### 2. 多个数组交集 该方法支持传入多个数组参数,返回所有数组的共同元素: ```javascript const arr3 = [2, 3, 5]; const multiIntersection = _.intersection(arr1, arr2, arr3); console.log(multiIntersection); // 输出: [2, 3] ``` --- ### 3. 保留重复元素的交集(类似LeetCode 350题) 若需保留重复元素(如元素 `2` 出现两次),需自定义逻辑: 1. 先获取唯一交集; 2. 遍历交集元素,统计在两个数组中的最小出现次数; 3. 按次数填充结果数组。 ```javascript function intersectWithDuplicates(arr1, arr2) { const uniqueIntersection = _.intersection(arr1, arr2); const countMap = new Map(); // 统计arr1中元素的出现次数 arr1.forEach(num => { if (uniqueIntersection.includes(num)) { countMap.set(num, (countMap.get(num) || 0) + 1); } }); // 统计并取最小值 const result = []; uniqueIntersection.forEach(num => { const countInArr2 = arr2.filter(x => x === num).length; const minCount = Math.min(countMap.get(num), countInArr2); result.push(...Array(minCount).fill(num)); }); return result; } // 示例 console.log(intersectWithDuplicates([1,2,2,3], [2,2,3])); // 输出: [2,2,3] ``` --- ### 4. 引用说明 - `_.intersection` 的底层实现基于哈希表,时间复杂度为 $O(n)$[^3]; - 若需处理更复杂的数据结构(如对象数组),可结合 `_.intersectionWith` 使用自定义比较器[^5]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Haooog

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

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

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

打赏作者

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

抵扣说明:

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

余额充值