Python:2个列表的交集,保留两个列表中的重复项

时间:2015-04-23 17:14:26

标签: python list duplicates intersection

我想有效地找到两个列表的交集,保留两者的重复项,例如A = [1,1,2,3],B = [1,1,2,4]应返回[1,1,1,1,2]

我知道之前曾问过类似的问题(Python intersection of two lists keeping duplicates) 但这对我没有帮助,因为只保留一个列表中的重复项。

以下作品

def intersect(A,B):
    C=[]
    for a in A:
        for b in B:
            if a==b:
                C.append(a)
    return C
但是,对于我正在做的事情来说,这还不够有效!为了加快速度,我尝试对列表进行排序

def intersect(A,B):
    A.sort()
    B.sort()
    C=[]
    i=0
    j=0
    while i<len(A) and j<len(B):
        if A[i]<=B[j]:
            if A[i]==B[j]: 
                C.append(A[i])
            i+=1
        else:
            j=j+1
    return C

然而,这只保留了列表B中的重复项。任何建议?

3 个答案:

答案 0 :(得分:3)

以下是您提出的问题的答案:

import collections
for A,B,expected_output in (
    ([1,1,2,3], [1,1,2,4], [1,1,1,1,2]),
    ([1,1,2,3], [1,2,4], [1,1,2])):
    cntA = collections.Counter(A)
    cntB = collections.Counter(B)
    output = [
        x for x in sorted(set(A) & set(B)) for i in range(cntA[x]*cntB[x])]
    assert output == expected_output

这是我自己和另外两个人最初解释的问题的答案:

import collections
A=[1,1,2,3]
B=[1,1,2,4]
expected_output = [1,1,1,1,2,2]
cntA = collections.Counter(A)
cntB = collections.Counter(B)
cnt_sum = collections.Counter(A) + collections.Counter(B)
output = [x for x in sorted(set(A) & set(B)) for i in range(cnt_sum[x])]
assert output == expected_output

您可以找到collections.Counter()文档herecollections是一个很棒的模块,我强烈建议您阅读整个模块的文档。

我意识到你实际上并不需要找到集合的交集,因为根据文档“缺少元素的数量是零”:

import collections
for A,B,expected_output in (
    ([1,1,2,3], [1,1,2,4], [1,1,1,1,2]),
    ([1,1,2,3], [1,2,4], [1,1,2])):
    cntA = collections.Counter(A)
    cntB = collections.Counter(B)
    output = [
        x for x in sorted(set(A)) for i in range(cntA[x]*cntB[x])]
    assert output == expected_output

答案 1 :(得分:2)

这个怎么样:

a_set = set(A)
b_set = set(B)
intersect = [i for i in A if i in b_set] + [j for j in B if j in a_set]

连接两个列表推导。一些额外的时间和内存用于创建A和B的集合,但这将远远超过检查集合与列表中项目的成员资格的效率。

你也可以稍微修饰一下:

set_intersect = set(A) & set(B)
list_intersect = [ele for ele in A+B if ele in set_intersect]

将两个列表强制转换为集合,然后使用列表推导来添加列表A和B中的所有元素(如果它们出现在集合交集中)。

答案 2 :(得分:0)

我很难加速你的代码,因为我不知道你在运行什么。无论是在小型列表还是大型列表上运行它,以及有多少不同的元素,它都会产生很大的不同。无论如何,这里有一些建议:

1

def intersect(a, b):
    count_a = Counter(a)
    count_b = Counter(b)
    count_mul = []
    for i in count_a:
        count_mul.extend([i] * (count_a[i] * count_b[i]))
    return count_mul

2

这将返回一个迭代器,您可以使用list(iterator)将其转换为列表

def intersect(a, b):
    count_a = Counter(a)
    count_b = Counter(b)
    count_mul = Counter()
    for i in count_a:
        count_mul[i] += count_a[i] * count_b[i]
    return count_mul.elements()

3

与您的方式非常相似,但不会更改需要时间的列表大小。

def intersect(A, B):
    return [a for a in A for b in B if a == b]

我不确定这会对您的原始方式有任何改进,这实际上取决于输入,但您的方式是O(n*m)而我的方式是O(n+m)

您可以使用模块timeit来检查它在输入上的运行速度:

from timeit import timeit
timeit('test.intersect(A, B)', 'import test; A = [1,1,2,3]; B = [1,1,2,4]')