【问题标题】:Removing common elements from lists从列表中删除公共元素
【发布时间】:2012-10-04 08:10:30
【问题描述】:

使用这些列表:

a=[2,6,79,10]
b=[6,7,2,0,8,5]

期望的输出是:

a=[79,10]
b=[7,0,8,5]

为什么这段代码不起作用?

def cel(a,b):
    for x in a:
        if x in b:
            b.remove(x)
            a.remove(x)

【问题讨论】:

  • 你的代码应该做什么?顶部的示例是所需的输出,还是运行时得到的?
  • 在迭代列表时不能修改它。
  • @dpassage 我需要从列表中删除常见元素

标签: python list python-2.7


【解决方案1】:

您可以为此目的使用集合操作:

i = set(a).intersection(set(b))
a = list(set(a).difference(i))
b = list(set(b).difference(i))

编辑我尝试调试您的原始代码并意识到它在删除一个数字时会跳过一个数字。谷歌搜索后,我发现 在迭代时修改列表不是一种定义的行为,因为存在一些内部索引问题。最简单的解决方法是在 for 循环中使用原始数组的副本:

for x in a[:]:
    if x in b:
        b.remove(x)
        a.remove(x)

【讨论】:

  • 不过,这会丢失原始订单(这可能会或可能不会重要。)
  • 如果我需要正确的顺序呢?
  • 最后一个例子有O(n**3)运行时间。 It can be done in O(n)
  • for x in a[:]: if b.count(x) > 0: b.remove(x) a.remove(x)
【解决方案2】:

@gokcehan 的答案中保留顺序的算法是三次方O(n**3)。即使对于中等大小的列表,它也非常低效(Programming Pearls 书中有一个示例,其中 Basic 中的线性算法优于 C 中的三次算法(分钟与天)。

您可以保持顺序并在线性时间内运行它:

common = set(a).intersection(b)
a = [x for x in a if x not in common]
b = [x for x in b if x not in common]

你可以就地做:

def remove_items(lst, items):
    items = set(items) # unnecessary in your case
    pos = 0
    for x in lst:
        if x not in items:
           lst[pos] = x # save
           pos += 1
    del lst[pos:]

common = set(a).intersection(b)
remove_items(a, common)
remove_items(b, common)

demo

【讨论】:

  • 我喜欢你的解决方案,但我认为它们的可读性很差。请记住O(n^3) 仍然是多项式算法,因此在大多数情况下它是可以接受的。需要小心避免过早优化。
  • @gokcehan:基于列表理解的解决方案既简单又高效。考虑到必须对其进行调试才能使其正确,建议O(n**3) 解决方案更可取是不合理的。第二个例子要复杂得多,幸运的是它几乎不需要。它在那里展示了如何就地完成。
  • O(n^3) 解决方案在那里显示了与已指出的原始算法最接近的修复,而不是作为一种更好的方式。同样,这个问题不是关于最有效的方法,因此您的第二个解决方案对我来说或多或少是垃圾。但是,我同意您的第一个解决方案与我建议的第一个解决方案一起非常有用,并且您的解决方案可能是最好的解决方案,以防订单很重要,但事实证明这个问题并非如此。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-03-05
相关资源
最近更新 更多