如何在列表的列表中找到共同的元素?

47

我正在尝试找出如何比较n个列表以查找公共元素。

p=[ [1,2,3],
    [1,9,9],
      ..
      ..
    [1,2,4]

>> print common(p)
>> [1]

现在如果我知道元素的数量,我可以进行比较,例如:

for a in b:
  for c in d:
    for x in y:
...

但是如果我不知道p有多少个元素,那么这种方法就行不通。我看过这个比较两个列表的解决方案 https://dev59.com/N3M_5IYBdhLWcg3waSb9#1388864

但是花费了4个小时尝试将其递归化,仍然找不到解决方案,所以任何帮助都将不胜感激!


可能是重复的问题:Python:如何找到列表交集? - Daniel A. White
你的解决方案必须是递归的吗?你能使用内置的“intersect”函数吗(也就是说,这是作业吗)? - K Mehta
我不知道正确的术语是“交集”,所以谢谢你告诉我。这会帮助我更深入地研究它。现在,它不一定要是递归的,但我们刚学习了递归,所以我想也许我需要比较p[0]和p[1],然后将结果传递给其余的元素,所以我认为可能是一个递归解决方案。 - 8bits
7个回答

71
你需要查找所有子列表的交集,而进行集合操作时应使用set数据类型:
result = set(p[0])
for s in p[1:]:
    result.intersection_update(s)
print result

谢谢回复。我对集合一无所知,所以我会继续研究。然而,从初步测试 p = [[1,2,3], [1,3], [8,1]] 来看,你提出的解决方案返回的是 [8,1] 而不是 [1]? - 8bits
@user1320800:这个答案的第一个版本在结尾处有一个错误的 print 语句。当然,我们必须打印 result,而不是 s - Sven Marnach
2
另外,result &= s - Joel Cornett
谢谢你的帮助!它很好地运行了,现在我知道什么是集合 :) - 8bits
Raymond的答案利用了Python 2.6+中set.intersection的能力,从而完全避免了循环。 - agf
补充@JoelCornett的评论,你可以使用result &= set(s)代替result.intersection_update(s) - Box Box Box Box

29

一个简单的解决方案(一行代码)是:

set.intersection(*[set(list) for list in p])

26

set.intersection() 方法支持同时对多个输入求交集。使用 参数解包 将子列表从外部列表中取出,并将它们作为单独的参数传递给 set.intersection()

>>> p=[ [1,2,3],
        [1,9,9],
        [1,2,4]]

>>> set(p[0]).intersection(*p)
set([1])

1
set(p[0]).intersection(*p)中的星号是一个解包操作符,如果有人在寻找一个“可搜索”的术语的话。 - Alex Witsil

17

为什么不直接这样做:

set.intersection(*map(set, p))

结果:

set([1])

或者像这样:

ip = iter(p)
s = set(next(ip))
s.intersection(*ip)

结果:

set([1])

编辑:

从控制台复制:

>>> p = [[1,2,3], [1,9,9], [1,2,4]]
>>> set.intersection(*map(set, p))
set([1])
>>> ip = iter(p)
>>> s = set(next(ip))
>>> s.intersection(*ip)
set([1])

我不知道是否有什么遗漏,但是传递 p=[ [1,2,3], [1,9,9], [1,2,4]] 似乎没有起作用。 - 8bits

3
p=[ [1,2,3],
    [1,9,9],
    [1,2,4]]

ans = [ele[0] for ele in zip(*p) if len(set(ele)) == 1]

结果:

>>> ans
[1]

1
尝试使用 p = [[1,2],[2,1]]。或者甚至是 p = [[1,2],[2]]。 - DSM
我的代码只有在我们查看所有共同元素且它们位于相同位置时才能正常工作;这就是zip(*p)的全部要点。我认为这就是OP想要的,但再次阅读帖子后,我可能误解了。我还假设每个子列表具有相同的长度。 - Akavall
此外,如果列表长度不同,zip函数将会丢弃元素。 - Joel Cornett
准确地说,子列表的长度并不总是相同的,尽管如此,非常感谢您的帮助! - 8bits
这是一个非常好的答案,适用于不同的问题。我使用它来查找共同的列:[len(set(c))==1 for c in zip(*p)] - Konchog
例如,要获取每个公共列的索引(作为列表)... [i for i,c in enumerate(zip(*q)) if len(set(c))==1] - Konchog

2
reduce(lambda x, y: x & y, (set(i) for i in p))

1
许多人认为reduce在Python中不够优雅。而且,你的版本需要将每个列表转换为一个集合,并创建一个额外的新集合来进行交集操作。Sven的版本只创建了一个集合。 - agf
@agf:明白了。尽管有点低效,但它仍然是一个可行的解决方案。 - Joel Cornett

0

你正在寻找所有子列表的交集,而进行集合操作时应使用的数据类型是set:

result = set(p[0])  
for s in p[1:]:
   result.intersection_update(s)
print result

然而,列表中最多只能有10个列表。如果超过这个限制,'result'列表将会无序。假设您已经通过list(result)将'result'转换为列表。

请确保使用result.sort()对其进行排序,以确保它是有序的,如果您依赖于它是有序的话。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接