Leetcode-数据结构-350. 两个数组的交集 II

问题

给两个整数数组 nums1 和 nums2 ,请以 数组形式 返回两数组的 交集

(其在交集中出现的次数:等于该数字在两个数组中出现次数的最小值)。

返回结果中 每个元素出现的次数(for遍历),应与元素在两个数组中 都 出现的次数一致(如果出现次数不一致,则考虑取 较小值 )。

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

代码

main函数

package DataStructure_start;

import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

public class DS20230112 {

    public static void main(String[] args) {

//        特点:数组有序
        int[] nums1 = {1,2,2,2,3,6};
        int[] nums2 = {2,2,5,6};

        int[] intersect = intersect(nums1, nums2);
        int[] intersect2 = intersect2(nums1, nums2);

        System.out.println(Arrays.toString(intersect));
        System.out.println(Arrays.toString(intersect2));
    }
}

方法一:哈希表

由于同一个数字在两个数组中都可能出现多次,因此需要用哈希表存储每个数字出现的次数。

对于一个数字,其在交集中出现的次数等于该数字在两个数组中出现次数的最小值。

首先遍历第一个数组,

并在哈希表中记录第一个数组中的每个数字以及对应出现的次数,

然后遍历第二个数组,

对于第二个数组中的每个数字,

如果在哈希表中存在这个数字,

则将该数字添加到答案,并减少哈希表中该数字出现的次数。

为了降低空间复杂度,首先遍历较短的数组并在哈希表中记录每个数字以及对应出现的次数,然后遍历较长的数组得到交集。

复杂度分析

时间复杂度:

O(m+n),其中 m 和 n 分别是两个数组的长度。

需要遍历两个数组并对哈希表进行操作,哈希表操作的时间复杂度是 O(1),因此 总时间复杂度 与 两个数组的长度和 呈线性关系。

空间复杂度:

O(min(m,n)),其中 m 和 n 分别是两个数组的长度。

对较短的数组进行哈希表的操作,哈希表的大小不会超过较短的数组的长度。

为返回值创建一个数组 intersection,其长度为较短的数组的长度。

public static int[] intersect(int[] nums1, int[] nums2) {

//        如果 nums1 大于 nums2
        if (nums1.length > nums2.length) {
//            则交换
            return intersect(nums2, nums1);
        }

        Map<Integer, Integer> map = new HashMap<Integer, Integer>();

//        遍历较短的数组 放入 数组num1,即根据 nums1 构建哈希表
        for (int num : nums1) {
//            每个元素在 集合map 中对应的个数为默认值为0,
//            如果有重复的数,每次重复 count + 1
            int count = map.getOrDefault(num, 0) + 1;
//            将 nums1 里的每个元素添加到 map集合 中
            map.put(num, count);
        }

        int[] intersection = new int[nums1.length];
//        初始化 index = 0 记录 交集的个数
        int index = 0;

//        遍历 数组nums2 逐个检查元素是否在数组中存在
        for (int num : nums2) {
            int count = map.getOrDefault(num, 0);
//            若存在,则计数为正
            if (count > 0) {
//                将元素拷贝到 交集数组 里
                intersection[index++] = num;
//                并减少 nums1 中对应元素的个数
                count--;
                if (count > 0) {
                    map.put(num, count);
                } else {
                    map.remove(num);
                }
            }
        }

//        返回 nums1 前index个 元素
        return Arrays.copyOfRange(intersection, 0, index);
    }

补充:

getOrDefault:

对不存在的键值提供默认值的方法。

由于该方法判定条件是 只要满足获取的值不为空 或者 包含对应的key 则不返回默认值

    default V getOrDefault(Object key, V defaultValue) {
        V v;
        return (((v = get(key)) != null) || containsKey(key))
                ? v
                : defaultValue;
    }

方法二:排序 + 双指针

如果两个数组是有序的★,则可以使用双指针的方法得到两个数组的交集。

首先对两个数组进行排序,然后使用两个指针遍历两个数组。

初始时,两个指针分别指向两个数组的头部。

每次比较两个指针指向的两个数组中的数字,

如果两个数字不相等,则将指向较小数字的指针右移一位,

如果两个数字相等,将该数字添加到答案,并将两个指针都右移一位。

当至少有一个指针超出数组范围时,遍历结束。

复杂度分析

时间复杂度:

O(mlogm+nlogn),其中 m 和 n 分别是两个数组的长度。

对两个数组进行排序的时间复杂度是 O(mlogm+nlogn),

遍历两个数组的时间复杂度是 O(m+n)O(m+n),因此总时间复杂度是 O(mlogm+nlogn)。

空间复杂度:

O(min(m,n)),其中 m 和 n 分别是两个数组的长度。

为返回值创建一个数组 intersection,其长度为较短的数组的长度。

不过在 C++ 中,我们可以直接创建一个 vector,不需要把答案临时存放在一个额外的数组中,所以这种实现的空间复杂度为 O(1)。

public static int[] intersect2(int[] nums1, int[] nums2) {

//        首先对两个数组进行排序
        Arrays.sort(nums1);
        Arrays.sort(nums2);

        int length1 = nums1.length, length2 = nums2.length;
        int[] intersection = new int[Math.min(length1, length2)];
//        头指针
        int index1 = 0, index2 = 0, index = 0;

//        使用两个指针遍历元素
        while (index1 < length1 && index2 < length2) {
//            如果 nums1 中当前元素的值 小于 nums2 中当前元素的值
            if (nums1[index1] < nums2[index2]) {
//                则将 nums1 的指针向后移一位
                index1++;
//            如果 nums1 中当前元素的值 大于 nums2 中当前元素值
            } else if (nums1[index1] > nums2[index2]) {
//                则将 nums2 的指针后移一位
                index2++;
//            如果 两个数组中的值相等
            } else {
//                则保存在 结果集intersetion 中
                intersection[index] = nums1[index1];
//                同时 nums1 nums2 结果集intersection 的指针后移
                index1++;
                index2++;
                index++;
            }
        }
        return Arrays.copyOfRange(intersection, 0, index);
    }

}

补充:

copyOfRange(original,int from,int to):

该方法是从original数组的下标from开始赋值,到下标to结束,不包括下标为to的元素

    public static int[] copyOfRange(int[] original, int from, int to) {
        int newLength = to - from;
        if (newLength < 0)
        throw new IllegalArgumentException(from + " > " + to);
        int[] copy = new int[newLength];
        System.arraycopy(original, from, copy, 0,
        Math.min(original.length - from, newLength));
        return copy;
    }

运行结果:

[2, 2, 6]
[2, 2, 6]

Process finished with exit code 0

结语:

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

那么就无法高效地对 nums2 进行排序,因此推荐使用方法一而不是方法二。

在方法一中,nums2 只关系到查询操作,因此每次读取 nums2 中的一部分数据,并进行处理即可。

参考:

来源:力扣(LeetCode)

链接:https://leetcode.cn/problems/intersection-of-two-arrays-ii

著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值