给定两个输入列表,如何创建两个输入共用的元素列表?

例如:对于输入[1,2,3,4,5,6]和[3,5,7,9],结果应该是[3,5];输入[‘这个’,‘这’,' n ', ', ']和[‘这个’,‘不’,‘,’,','],结果应该[‘这个’,‘,’]。


参见: 在Python中,如何从两个列表中找到常用词,同时保持词序?(维持秩序) Python -多个列表的交集?(用于计算>= 3个列表之间的交集) 两个包含重复项的列表的交集?(保持重复元素)


当前回答

我比较了每个答案提到的每一种方法。目前,我使用python 3.6.3来实现这个功能。这是我使用的代码:

import time
import random
from decimal import Decimal


def method1():
    common_elements = [x for x in li1_temp if x in li2_temp]
     print(len(common_elements))


def method2():
    common_elements = (x for x in li1_temp if x in li2_temp)
    print(len(list(common_elements)))


def method3():
    common_elements = set(li1_temp) & set(li2_temp)
    print(len(common_elements))


def method4():
    common_elements = set(li1_temp).intersection(li2_temp)
    print(len(common_elements))


if __name__ == "__main__":
    li1 = []
    li2 = []
    for i in range(100000):
        li1.append(random.randint(0, 10000))
        li2.append(random.randint(0, 10000))

    li1_temp = list(set(li1))
    li2_temp = list(set(li2))

    methods = [method1, method2, method3, method4]
    for m in methods:
        start = time.perf_counter()
        m()
        end = time.perf_counter()
        print(Decimal((end - start)))

如果你运行这段代码,你可以看到如果你使用列表或生成器(如果你迭代生成器,而不是仅仅使用它)。当我强迫生成器打印它的长度时,我这样做了),你会得到几乎相同的性能。但是如果你使用set,你会得到更好的性能。如果你使用交叉方法你会得到更好的性能。在我的电脑中,每种方法的结果如下:

method1: 0.8150673999999999974619413478649221360683441 method2: 0.8329545000000001531148541289439890533685684 method3: 0.0016547000000000089414697868051007390022277 method4: 0.0010262999999999244948867271887138485908508

其他回答

使用发电机:

common = (x for x in list1 if x in list2)

这样做的好处是,即使在使用庞大的列表或其他庞大的可迭代对象时,它也会在常数时间内(几乎立即)返回生成器。

例如,

list1 =  list(range(0,10000000))
list2=list(range(1000,20000000))
common = (x for x in list1 if x in list2)

对于list1和list2的这些值,这里的所有其他答案都将花费很长时间。

然后,您可以用

for i in common: print(i)

集合是另一种解法

a = [3,2,4]
b = [2,3,5]
set(a)&set(b)
{2, 3}

这里有一些解决方案是在O(l1+l2)不计算重复项目,而缓慢的解决方案(至少O(l1*l2),但可能更昂贵)确实考虑重复项目。

所以我想我应该添加一个O(l1*log(l1)+l2*(log(l2))解。如果列表已经排序,这尤其有用。

def common_elems_with_repeats(first_list, second_list):
    first_list = sorted(first_list)
    second_list = sorted(second_list)
    marker_first = 0
    marker_second = 0
    common = []
    while marker_first < len(first_list) and marker_second < len(second_list):
        if(first_list[marker_first] == second_list[marker_second]):
            common.append(first_list[marker_first])
            marker_first +=1
            marker_second +=1
        elif first_list[marker_first] > second_list[marker_second]:
            marker_second += 1
        else:
            marker_first += 1
    return common

另一个更快的解决方案包括从list1创建一个item->计数映射,并遍历list2,同时更新映射和计数。不需要排序。需要额外的内存,但技术上是O(l1+l2)

S.Mark和SilentGhost建议的解决方案通常会告诉您应该如何以Pythonic方式完成,但我认为您也可以从了解为什么您的解决方案不起作用中受益。问题是,一旦找到两个列表中的第一个公共元素,就只返回该元素。您的解决方案可以通过创建一个结果列表并收集该列表中的公共元素来修复:

def common_elements(list1, list2):
    result = []
    for element in list1:
        if element in list2:
            result.append(element)
    return result

一个使用列表推导式的更简短的版本:

def common_elements(list1, list2):
    return [element for element in list1 if element in list2]

然而,正如我所说,这是一种非常低效的方式——Python的内置集类型更高效,因为它们是在C内部实现的。

1) Method1 保存list1是字典,然后迭代list2中的每个elem

def findarrayhash(a,b):
    h1={k:1 for k in a}
    for val in b:
        if val in h1:
            print("common found",val)
            del h1[val]
        else:
            print("different found",val)
    for key in h1.iterkeys():
        print ("different found",key)

找到共同和不同的元素:

2) Method2 使用设置

def findarrayset(a,b):
    common = set(a)&set(b)
    diff=set(a)^set(b)
    print list(common)
    print list(diff)