【Lintcode】548. Intersection of Two Arrays II

题目地址:

https://www.lintcode.com/problem/intersection-of-two-arrays-ii/description

给定两个数组,求其交集。重复次数也要计入。

法1:先遍历其中一个数组,用哈希表记录每个数字出现的次数,然后遍历第二个数组,先check数字是否在哈希表里,如果不在,那就说明这个数字不在交集里,略过;否则说明其为交集中的元素,所以加入最终结果,并且将哈希表里记录的次数减掉 1 1 1,如果哈希表里某个数字出现次数为 0 0 0则删掉这个entry。代码如下:

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class Solution {
    /**
     * @param nums1: an integer array
     * @param nums2: an integer array
     * @return: an integer array
     */
    public int[] intersection(int[] nums1, int[] nums2) {
        // write your code here
        if (nums1 == null || nums2 == null || nums1.length == 0 || nums2.length == 0) {
            return new int[0];
        }
		// 为了节省空间,我们维持nums1是较短的那个数组
		if (nums1.length > nums2.length) {
            int[] tmp = nums1;
        	nums1 = nums2;
        	nums2 = tmp;
        }
        // 用哈希表记录nums1里各个数字出现的个数
        Map<Integer, Integer> map = new HashMap<>();
        for (int num : nums1) {
            map.put(num, map.getOrDefault(num, 0) + 1);
        }
        // 用res记录最终结果
        List<Integer> res = new ArrayList<>();
        for (int num : nums2) {
        	// 如果map中存在num,说明其为交集元素,加入res并将其在map中的计数减一
            if (map.containsKey(num)) {
                res.add(num);
                map.put(num, map.get(num) - 1);
                // 如果减到0了就将其删掉
                if (map.get(num) == 0) {
                    map.remove(num);
                }
            }
        }
        
        int[] ans = new int[res.size()];
        for (int i = 0; i < res.size(); i++) {
            ans[i] = res.get(i);
        }
        
        return ans;
    }
}

时空复杂度 O ( min ⁡ ( m , n ) ) O(\min(m,n)) O(min(m,n))

法2:排序 + 二分。思路是先将两个数组排序,然后遍历较短的数组 A A A,并且用二分法check每个数 A [ i ] A[i] A[i]是否出现在了另一个数组 B B B中,具体来说,是找 A [ i ] A[i] A[i] B B B中第一次出现的位置 p p p。如果在,则这个数属于交集,加入最终答案,并且在下一次做二分法的时候从 B [ p + 1 ] B[p+1] B[p+1]开始找,避免重复找到同一个位置的数字。当 A A A遍历完毕,或者 p p p已经到 B B B的末尾时(指的是 p = B . l e n g t h − 1 p=B.length-1 p=B.length1,这时说明 A A A之后的数字在 B B B中已经不可能找到对应的数字了),退出循环。代码如下:

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class Solution {
    /**
     * @param nums1: an integer array
     * @param nums2: an integer array
     * @return: an integer array
     */
    public int[] intersection(int[] nums1, int[] nums2) {
        // write your code here
        if (nums1 == null || nums2 == null || nums1.length == 0 || nums2.length == 0) {
            return new int[0];
        }
    
        if (nums1.length > nums2.length) {
            int[] tmp = nums1;
            nums1 = nums2;
            nums2 = tmp;
        }
    
        Arrays.sort(nums1);
        Arrays.sort(nums2);
    	// start表示从nums2[start]开始进行二分查找
        int start = 0;
        List<Integer> res = new ArrayList<>();
        for (int i = 0; i < nums1.length; i++) {
            int pos = binarySearch(nums2, start, nums2.length - 1, nums1[i]);
            // 如果pos != -1说明能够找到,那么加入res,并且将下一次二分查找的范围缩小;
            // 如果start到达了nums2末尾,以后的二分查找都不可能找到结果了,直接退出循环
            if (pos != -1) {
                res.add(nums1[i]);
                start = pos + 1;
                if (start == nums2.length) {
                    break;
                }
            }
        }
        
        int[] ans = new int[res.size()];
        for (int i = 0; i < res.size(); i++) {
            ans[i] = res.get(i);
        }
        return ans;
    }
    // 从nums[l, r]中寻找第一次k出现位置
    private int binarySearch(int[] nums, int l, int r, int k) {
        while (l < r) {
            int m = l + (r - l >> 1);
            if (nums[m] < k) {
                l = m + 1;
            } else {
                r = m;
            }
        }
        
        return nums[l] == k ? l : -1;
    }
    
}

时间复杂度 O ( m log ⁡ m + n log ⁡ n + m log ⁡ n ) = O ( m log ⁡ m + ( n + m ) log ⁡ n ) O(m\log m+n\log n+m\log n)=O(m\log m+(n+m)\log n) O(mlogm+nlogn+mlogn)=O(mlogm+(n+m)logn),其中 m < n m<n m<n m m m为较短数组的长度, n n n为较长数组的长度。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值