两个数组的交集(Leetcode 349)
方法一 两个集合
python版本
class Solution:
def intersection(self, nums1: List[int], nums2: List[int]) -> List[int]:
set1 = set(nums1)
set2 = set(nums2)
return self.set_intersection(set1, set2)
def set_intersection(self, set1, set2):
if len(set1) > len(set2):
return self.set_intersection(set2, set1)
return [x for x in set1 if x in set2]
java版本
class Solution {
public int[] intersection(int[] nums1, int[] nums2) {
Set<Integer> set1 = new HashSet<Integer>();
Set<Integer> set2 = new HashSet<Integer>();
for (int num : nums1) {
set1.add(num);
}
for (int num : nums2) {
set2.add(num);
}
return getIntersection(set1, set2);
}
public int[] getIntersection(Set<Integer> set1, Set<Integer> set2) {
if (set1.size() > set2.size()) {
return getIntersection(set2, set1);
}
Set<Integer> intersectionSet = new HashSet<Integer>();
for (int num : set1) {
if (set2.contains(num)) {
intersectionSet.add(num);
}
}
int[] intersection = new int[intersectionSet.size()];
int index = 0;
for (int num : intersectionSet) {
intersection[index++] = num;
}
return intersection;
}
}
方法二 排序+双指针
java版本
class Solution {
public int[] intersection(int[] nums1, int[] nums2) {
Arrays.sort(nums1);
Arrays.sort(nums2);
int length1 = nums1.length, length2 = nums2.length;
int[] intersection = new int[length1 + length2];
int index = 0, index1 = 0, index2 = 0;
while (index1 < length1 && index2 < length2) {
int num1 = nums1[index1], num2 = nums2[index2];
if (num1 == num2) {
// 保证加入元素的唯一性
if (index == 0 || num1 != intersection[index - 1]) {
intersection[index++] = num1;
}
index1++;
index2++;
} else if (num1 < num2) {
index1++;
} else {
index2++;
}
}
return Arrays.copyOfRange(intersection, 0, index);
}
}
python版本
class Solution:
def intersection(self, nums1: List[int], nums2: List[int]) -> List[int]:
nums1.sort()
nums2.sort()
length1, length2 = len(nums1), len(nums2)
intersection = list()
index1 = index2 = 0
while index1 < length1 and index2 < length2:
num1 = nums1[index1]
num2 = nums2[index2]
if num1 == num2:
# 保证加入元素的唯一性
if not intersection or num1 != intersection[-1]:
intersection.append(num1)
index1 += 1
index2 += 1
elif num1 < num2:
index1 += 1
else:
index2 += 1
return intersection
https://leetcode-cn.com/problems/intersection-of-two-arrays
两个数组的交集II(Leetcode 350)
方法一:哈希表
java版本
class Solution {
public int[] intersect(int[] nums1, int[] nums2) {
if (nums1.length > nums2.length) {
return intersect(nums2, nums1);
}
Map<Integer, Integer> map = new HashMap<Integer, Integer>();
for (int num : nums1) {
int count = map.getOrDefault(num, 0) + 1;
map.put(num, count);
}
int[] intersection = new int[nums1.length];
int index = 0;
for (int num : nums2) {
int count = map.getOrDefault(num, 0);
if (count > 0) {
intersection[index++] = num;
count--;
if (count > 0) {
map.put(num, count);
} else {
map.remove(num);
}
}
}
return Arrays.copyOfRange(intersection, 0, index);
}
}
python版本
class Solution:
def intersect(self, nums1: List[int], nums2: List[int]) -> List[int]:
if len(nums1) > len(nums2):
return self.intersect(nums2, nums1)
m = collections.Counter()
for num in nums1:
m[num] += 1
intersection = list()
for num in nums2:
if (count := m.get(num, 0)) > 0:
intersection.append(num)
m[num] -= 1
if m[num] == 0:
m.pop(num)
return intersection
方法二:排序
python版本
class Solution:
def intersect(self, nums1: List[int], nums2: List[int]) -> List[int]:
nums1.sort()
nums2.sort()
length1, length2 = len(nums1), len(nums2)
intersection = list()
index1 = index2 = 0
while index1 < length1 and index2 < length2:
if nums1[index1] < nums2[index2]:
index1 += 1
elif nums1[index1] > nums2[index2]:
index2 += 1
else:
intersection.append(nums1[index1])
index1 += 1
index2 += 1
return intersection
java版本
class Solution {
public int[] intersect(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) {
if (nums1[index1] < nums2[index2]) {
index1++;
} else if (nums1[index1] > nums2[index2]) {
index2++;
} else {
intersection[index] = nums1[index1];
index1++;
index2++;
index++;
}
}
return Arrays.copyOfRange(intersection, 0, index);
}
}
https://leetcode-cn.com/problems/intersection-of-two-arrays-ii
超长数组求交集
方法一 :利用python的counter函数
from collections import Counter
c = Counter([1,2,3])
d = Counter([2,3,4])
res= c&d
print(list(res))
更多用法
>>> c = Counter() # 创建一个新的空counter
>>> c = Counter('abcasdf') # 一个迭代对象生成的counter
>>> c = Counter({'red': 4, 'yello': 2}) # 一个映射生成的counter
>>> c = Counter(cats=2, dogs=5) # 关键字参数生成的counter
# counter 生成counter, 虽然这里并没有什么用
>>> from collections import Counter
>>> c = Counter('abcasd')
>>> c
Counter({'a': 2, 'c': 1, 'b': 1, 's': 1, 'd': 1})
>>> c2 = Counter(c)
>>> c2
Counter({'a': 2, 'c': 1, 'b': 1, 's': 1, 'd': 1})
因为 Counter 实现了字典的 missing 方法, 所以当访问不存在的key的时候,返回值为0:
>>> c = Counter(['apple', 'pear'])
>>> c['orange']
0
counter 常用的方法:
# elements() 按照counter的计数,重复返回元素
>>> c = Counter(a=4, b=2, c=0, d=-2)
>>> list(c.elements())
['a', 'a', 'a', 'a', 'b', 'b']
# most_common(n) 按照counter的计数,按照降序,返回前n项组成的list; n忽略时返回全部
>>> Counter('abracadabra').most_common(3)
[('a', 5), ('r', 2), ('b', 2)]
# subtract([iterable-or-mapping]) counter按照相应的元素,计数相减
>>> c = Counter(a=4, b=2, c=0, d=-2)
>>> d = Counter(a=1, b=2, c=3, d=4)
>>> c.subtract(d)
>>> c
Counter({'a': 3, 'b': 0, 'c': -3, 'd': -6})
# update([iterable-or-mapping]) 不同于字典的update方法,这里更新counter时,相同的key的value值相加而不是覆盖
# 实例化 Counter 时, 实际也是调用这个方法
# Counter 间的数学集合操作
>>> c = Counter(a=3, b=1, c=5)
>>> d = Counter(a=1, b=2, d=4)
>>> c + d # counter相加, 相同的key的value相加
Counter({'c': 5, 'a': 4, 'd': 4, 'b': 3})
>>> c - d # counter相减, 相同的key的value相减,只保留正值得value
Counter({'c': 5, 'a': 2})
>>> c & d # 交集: 取两者都有的key,value取小的那一个
Counter({'a': 1, 'b': 1})
>>> c | d # 并集: 汇聚所有的key, key相同的情况下,取大的value
Counter({'c': 5, 'd': 4, 'a': 3, 'b': 2})
常见做法:
sum(c.values()) # 继承自字典的.values()方法返回values的列表,再求和
c.clear() # 继承自字典的.clear()方法,清空counter
list(c) # 返回key组成的list
set(c) # 返回key组成的set
dict(c) # 转化成字典
c.items() # 转化成(元素,计数值)组成的列表
Counter(dict(list_of_pairs)) # 从(元素,计数值)组成的列表转化成Counter
c.most_common()[:-n-1:-1] # 最小n个计数的(元素,计数值)组成的列表
c += Counter() # 利用counter的相加来去除负值和0的值
https://www.zhihu.com/question/420013804
https://blog.csdn.net/jason3586596/article/details/80073517
方法二:位图法
BitSet bits1 = new BitSet(16);
BitSet bits2 = new BitSet(16);
// set some bits
for(int i=0; i<16; i++) {
if((i%2) == 0) bits1.set(i);
if((i%5) != 0) bits2.set(i);
}
System.out.println("Initial pattern in bits1: ");
System.out.println(bits1);
System.out.println("\nInitial pattern in bits2: ");
System.out.println(bits2);
//AND bits
bits2.and(bits1);
System.out.println("\nbits2 AND bits1: ");
System.out.println(bits2);
方法三:排序法
先把两个数组给排序,再用双指针,可以见下面
方法四:索引法
以空间换时间,把集合(感谢网友的指正,集合里面的元素是不重复的!)中的元素作为数组下表的索引。来看例子:
A= {1 ,12, 13, 25},那Asub[1] = 3,Asub[12] = 1 ,Asub[13] = 1 ,Asub[25] = 1 ;
B={1, 2, 3, 15 ,}那Bsub[1] = 1; Bsub[2] = 1; Bsub[3] = 1; Bsub[15] = 1;
对元素少的集合扫一遍,发现Asub[1] = 3 和Bsub[1] = 1有相同的索引1,并且重复度为1(因为一个集合当中没有重复元素),所以交集肯定包括{1, 1}; Bsub[2] = 1而Asub[2] = 0,表示无交集,依次类推,可以得到集合A和B的交集。
假设集合中存在负数,可以把集合分成正整数和负整数(加个负号变正整数)两部分,解法同上!
优点:速度快,时间复杂度O(N)
缺点:空间消耗大,以空间换取时间
这是我看到题目第一个想到的算法,再来想到排序法,而集合压缩是有感而发的,索引法的缺点是空间消耗多,原因是可能索引值太大,要申请很多的不必要的空间,这个缺点也是有克服的方法的,就是采用哈希查找,找到一个比较合适的哈希函数,把索引的值减小了,从而减少消耗的内存空间。比如哈希函数为f(x) = (x + MOD) % MOD (除留余数法,MOD为常数),还有平方取中法、折叠法等方法,然而,无论哈希函数设计有多么精细,都会产生冲突现象,也就是2个关键字处理函数的结果映射在了同一位置上,因此,有一些方法可以避免冲突。这里没有仔细钻研,只提供一些思路,有兴趣的朋友可以继续研究。
code:(我的代码仅适用与正整数部分,未处理负数)
C++代码
/*
Tencent: A、B两个整数集合,设计一个算法求他们的交集,尽可能的高效
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define M 6
#define N 5
int Mymin(int a, int b)
{
return a < b ? a : b;
}
int main(void)
{
int A[] = {1, 10, 12, 23, 5, 45};
int B[] = {1, 10, 12, 123, 52};
//find MaxNumber in A
int ifindA = 0;
int MaxInA = A[0];
for(ifindA = 0; ifindA < M; ifindA++)
{
MaxInA = MaxInA > A[ifindA] ? MaxInA : A[ifindA];
}
//find MaxNumber in B
int ifindB = 0;
int MaxInB = 0;
for(ifindB = 0; ifindB < M; ifindB++)
{
MaxInB = MaxInB > A[ifindB] ? MaxInB : A[ifindB];
}
int *AsubPositive = (int *)malloc(sizeof(int) * (MaxInA + 1));
int *BsubPositive = (int *)malloc(sizeof(int) * (MaxInB + 1));
memset(AsubPositive, 0, sizeof(int) * (MaxInA + 1));
memset(BsubPositive, 0, sizeof(int) * (MaxInB + 1));
//COPY Positive and Negative numbers of A
int i = 0;
for(i = 0; i < M; i++)
{
AsubPositive[A[i]]++;
}
//COPY Positive and Negative numbers of B
int j = 0;
for(j = 0; j < N; j++)
{
BsubPositive[B[j]]++;
}
int k = 0;
int icount = 0;
//扫描AsubNegative和BsubPositive
printf("the Intersection of A and B is : { ");
for(k = 0; k < M; k++)
{
//有交集输出该数
icount = Mymin(AsubPositive[A[k]], BsubPositive[A[k]]);
if(icount == 1)
{
printf("%-3d",A[k]);
}
A[k] = 0;
}
printf(" }");
return 0;
}
方法五:压缩集合法
见https://blog.csdn.net/jie1991liu/article/details/13168255
https://www.nowcoder.com/questionTerminal/6245d7bcae414235ab682b3f842d76bc?orderByHotValue=0&pos=159&mutiTagIds=578
https://blog.csdn.net/moli152_/article/details/48163351
https://blog.csdn.net/jie1991liu/article/details/13168255
有序数组求交集
方法一:如果可以输入重复的数字
输入:l1 = [1,3,4,5,7]
l2 = [2,5,6,7,9]
输出:[5,7]
def comNumber(l1,l2):
i=0
j=0
res = []
while i < len(l1) and j < len(l2):
if l1[i] == l2[j]:
res.append(l1[i])
i+=1
j+=1
elif l1[i]<l2[j]:
i+=1
else:
j+=1
return res
if __name__ == '__main__':
l1 = [1,3,4,5,2,4,1,7]
l2 = [2,5,2,6,7,9]
b=comNumber(l1,l2)
print(b)
方法二:不能输出重复的数字
思路:
双指针:p1指向arr1的开头,p2指向arr2的开头,然后进行比较,如果p1与p2指向的数相等,则将这个数加入结果集。然后p1++,p2++,再继续遍历。如果p1指向的数小于p2指向的数,则p1++,否则p2++;
实现:要求结果中不含重复的数,则选用TreeSet而不用ArrayList
public TreeSet<Integer> find(int[] arr1,int[] arr2){
TreeSet<Integer> set=new TreeSet<>();
int p1=0;
int p2=0;
while(p1<arr1.length&&p2<arr2.length){
if(arr1[p1]==arr2[p2]){
set.add(arr1[p1]);
p1++;
p2++;
}else if(arr1[p1]<arr2[p2]){
p1++;
}else{
p2++;
}
}
return set;
}
https://blog.csdn.net/weixin_40924580/article/details/84351584
https://blog.csdn.net/orangefly0214/article/details/89390586