我希望从另一个列表中减去列表但是要尊重重复:
>>> a = ['a', 'b', 'c','c', 'c', 'c', 'd', 'e', 'e']
>>> b = ['a', 'c', 'e', 'f','c']
>>> a - b
['b', 'c','c', 'd', 'e']
元素顺序无关紧要。
答案here有一个问题,但忽略了重复。解决方案会给出:
>>> a - b
['b', 'd']
一个解决方案考虑重复,但它会改变原始列表中的一个:
[i for i in a if not i in b or b.remove(i)]
我写了这个解决方案:
a_sub_b = list(a)
b_sub_a = list(b)
for e in a:
if e in b_sub_a:
a_sub_b.remove(e)
b_sub_a.remove(e)
print a_sub_b # a - b
print b_sub_a # b - a
这对我有用,但有更好的解决方案,更简单或更有效吗?
答案 0 :(得分:4)
如果订单无关紧要,请使用collections.Counter
:
c = list((Counter(a) - Counter(b)).elements())
Counter(a) - Counter(b)
构建一个计数器,其中元素x
的计数等于x
中a
显示的次数减去x
的次数出现在b
中。 elements()
创建一个迭代器,使每个元素的次数等于其计数,list
将其转换为列表。整件事需要O(len(a)+len(b))
次。
请注意,根据您的工作情况,最好不要使用列表,只需将a
,b
和c
表示为计数器。< / p>
答案 1 :(得分:2)
这将为b
的每个元素搜索a
的每个元素。对于匹配的每个元素,它还会在每个列表上执行线性remove
。因此,您的算法需要二次时间 - O(max(N, M)^2)
其中N
是a
的长度,M
是b
的长度。
如果您只是将b
复制到set
而不是list
,则可以解决问题。现在,您只需对a
中的每个元素执行常量时间集查找,并执行常量时间集删除而不是列表删除。但是,您仍然遇到线性时间和从a
副本中删除错误的问题。而且您无法将a
复制到set
,因为这会丢失重复项。
最重要的是,a_sub_b.remove(e)
删除与e
匹配的元素。这与您刚刚查找的元素的元素不一定相同。它将成为一个相等的元素,如果身份根本不重要,那很好......但如果确实如此,那么remove
可能做错了。
无论如何,性能已经足以成为不使用remove
的理由了。一旦解决了上述问题,这就是使算法成为二次而非线性的唯一方法。
解决此问题的最简单方法是建立一个新列表,而不是复制列表并从中删除。
解决这两个问题,你有O(2N+M)
时间,这是线性的。
所以,把两者放在一起:
b_set = set(b)
new_a = []
for element in a:
if a in b_set:
b_set.remove(element)
else:
new_a.append(element)
然而,这仍然可能有问题。你还没有很清楚地陈述事情,所以很难确定,但b
可能包含重复项,如果是这样,那是否意味着应该从{{1}中删除重复的元素}} 多次?如果是这样,您需要一个多集,而不是a
。在Python中执行此操作的最简单方法是使用set
:
Counter
另一方面,如果from collections import Counter
b_counts = Counter(b)
new_a = []
for element in a:
if b_counts[element]:
b_counts[element] -= 1
else:
new_a.append(element)
和a
的顺序都不重要,那么这只会减少到多重差异,这样会更容易:
b
但实际上,如果两者的顺序毫无意义,那么您可能应该首先使用new_a = list((Counter(a) - Counter(b)).elements())
或其他多重表示,而不是列表......
答案 2 :(得分:1)
以下仅使用标准库:
a = ['a', 'b', 'b', 'c', 'c', 'c', 'c', 'd', 'd', 'd', 'e', 'e']
b = ['a', 'c', 'e', 'f','c']
a_set = set(a)
b_set = set(b)
only_in_a = list(a_set - b_set)
diff_list = list()
for _o in only_in_a:
tmp = a.count(_o) * _o
diff_list.extend(tmp)
for _b in b_set:
tmp = (a.count(_b) - b.count(_b)) * _b
diff_list.extend(tmp)
print diff_list
并给出:
['b', 'b', 'd', 'd', 'd', 'c', 'c', 'e']
正如所料。