在Python中减去两个列表

52 投票
13 回答
162475 浏览
提问于 2025-04-15 18:03

在Python中,如何从两个不唯一且无序的列表中减去元素呢?比如我们有 a = [0,1,2,1,0]b = [0, 1, 1],我想做类似 c = a - b 的操作,结果 c 应该是 [2, 0] 或者 [0, 2],顺序对我来说无所谓。如果 a 中不包含 b 的所有元素,应该抛出一个异常。

注意,这和集合是不同的! 我并不是想找出 ab 中元素集合的差异,而是想找出 ab 中实际元素的差异。

我可以用一个循环来实现,先在 a 中查找 b 的第一个元素,然后把这个元素从 ba 中删除,依此类推。但我觉得这样做不太好,因为效率很低(时间复杂度是 O(n^2)),而实际上应该可以在 O(n log n) 的时间内完成这个操作。

13 个回答

42

我会用更简单的方法来做:

a_b = [e for e in a if not e in b ]

..正如上面提到的,这样做是不对的——只有当列表中的项目是唯一的时候才有效。如果项目是唯一的,使用下面的方法会更好:

a_b = list(set(a) - set(b))
66

我知道“for”不是你想要的,但它简单明了:

for x in b:
  a.remove(x)

或者如果b里的成员可能不在a里,那就用:

for x in b:
  if x in a:
    a.remove(x)
36

Python 2.7 和 3.2 版本新增了一个叫做 collections.Counter 的类,这个类是字典的一种变体,它可以把元素和它出现的次数对应起来。你可以把它当作一个多重集合来使用。你可以这样做:

from collections import Counter
a = Counter([0, 1, 2, 1, 0])
b = Counter([0, 1, 1])
c = a - b  # ignores items in b missing in a

print(list(c.elements()))  # -> [0, 2]

另外,如果你想检查 b 中的每个元素是否都在 a 中,可以这样做:

# a[key] returns 0 if key not in a, instead of raising an exception
assert all(a[key] >= b[key] for key in b)

但是如果你只能用 2.5 版本,你可以尝试导入这个类,如果导入失败的话,就自己定义一个版本。这样的话,如果有更新的版本可以用,你就能得到最新的版本;如果没有,你也能用一个能正常工作的版本。而且如果将来这个类被转换成 C 语言实现的话,你的程序运行速度也会更快。

try:
   from collections import Counter
except ImportError:
    class Counter(dict):
       ...

你可以在 这里 找到当前的 Python 源代码。

撰写回答