题目地址:
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.length−1,这时说明 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为较长数组的长度。