题目描述

✅ 2215. 找出两数组的不同

image-20260928234456612

image-20260928234456613

题意分析

返回两个列表:第一个包含只在 nums1 出现、完全不在 nums2 出现的不同数值;第二个方向相反。每个列表内部去重,元素顺序不限。

比较的是值是否出现,不是两个数组同一下标是否相等,也不比较次数多少。一个值只要在两边都出现,就不属于任何一侧的结果;即使某个结果为空,返回结构也必须保留两个列表。

解法:分别计算两个集合的差集

核心思路

[!blue]

题目同时需要“去重”和“判断另一边有没有”,分别将两个数组放入哈希集合即可完成这两件事。集合只保留不同值,重复出现次数不会影响后续判断。

遍历 set1 时,只把不存在于 set2 的值加入第一列表。每个值在 set1 中最多遍历一次,保证输出不重复;再以相反方向遍历 set2,得到第二列表。

两次判断都需要读取对方原本完整的集合。因此代码始终保留两个集合,不在第一轮删除共同元素,否则第二轮可能把原本共有的值误认为只属于第二边。数组本身不需要排序或修改,负数也能直接作为集合键。

解题步骤

  1. 分别将两个数组中的所有值加入 set1 和 set2。
  2. 遍历去重后的 set1,未被 set2 包含的值加入 only1。
  3. 遍历完整 set2,未被 set1 包含的值加入 only2。
  4. 按 [only1, only2] 的固定方向返回,即使某个列表为空也保留它。

代码实现

class Solution {
    public List<List<Integer>> findDifference(int[] nums1, int[] nums2) {
        Set<Integer> set1 = new HashSet<>();

        for (int num : nums1) {
            // 集合天生去重,负数也能直接当键,不需要下标偏移。
            set1.add(num);
        }

        Set<Integer> set2 = new HashSet<>();

        for (int num : nums2) {
            set2.add(num);
        }

        List<Integer> only1 = new ArrayList<>();

        for (int num : set1) {
            // 方向一:遍历已去重的 set1,查 set2 是均摊 O(1)。
            if (!set2.contains(num)) {
                only1.add(num);
            }
        }

        List<Integer> only2 = new ArrayList<>();

        for (int num : set2) {
            // 方向二与方向一完全对称;全程没有修改过集合,这里读到的 set1 是完整的。
            if (!set1.contains(num)) {
                only2.add(num);
            }
        }

        List<List<Integer>> answer = new ArrayList<>();

        // 即使某个列表为空也必须占位,返回值长度恒为 2。
        answer.add(only1);
        answer.add(only2);

        return answer;
    }
}
func findDifference(nums1 []int, nums2 []int) [][]int {
    set1 := make(map[int]struct{}, len(nums1))

    for _, num := range nums1 {
        // struct{} 不占空间,这里只用键来表示集合成员。
        set1[num] = struct{}{}
    }

    set2 := make(map[int]struct{}, len(nums2))

    for _, num := range nums2 {
        set2[num] = struct{}{}
    }

    // 初始化为空切片而非 nil,否则空结果会被序列化成 null。
    only1 := []int{}

    for num := range set1 {
        // 方向一:查 set2,双值写法可以区分「键不存在」。
        if _, ok := set2[num]; !ok {
            only1 = append(only1, num)
        }
    }

    only2 := []int{}

    for num := range set2 {
        // 方向二与方向一完全对称,只是两个集合角色互换。
        if _, ok := set1[num]; !ok {
            only2 = append(only2, num)
        }
    }

    return [][]int{
        only1,
        only2,
    }
}

复杂度分析

  • 时间复杂度:期望 $O(m+n)$,建集合扫描全部输入,再各扫描一次不同值,哈希查询平均为常数。
  • 空间复杂度:$O(m+n)$,两组集合与输出总规模不超过输入数量级。

关键点总结

[!green]

  • 将出现次数压缩成集合成员关系,恰好满足去重与存在性判断。
  • 两个差集的方向不同,不能把它们合并为一个集合。
  • 遍历集合而非原数组,天然避免输出重复值。
  • 查询期间保留完整原集合,避免两个方向的计算互相干扰。

易错点总结

[!yellow]

  • 按同一下标比较两个数组,无法判断一个值是否在对方其他位置出现。
  • 直接遍历原数组输出且不去重,同一个独有值可能重复加入。
  • 认为出现次数更多的一侧应保留共同值,本题只关心是否存在。
  • 第一轮删除共同元素后,用这个残缺集合计算第二方向,会把共同值错误输出。
  • 空结果列表不占位,或交换两个列表的位置,改变题目要求的返回结构。

相似题目

题目 难度 关联与区别
349. 两个数组的交集 简单 同样先按集合处理重复值,原题求交集,本题分别求两个方向的差集。
350. 两个数组的交集 II 简单 原题交集保留重数,本题只关心是否出现并要求去重。
转载与许可
作者
链接 https://hgnulb.github.io/blog/2026/10577606
许可 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处!