【问题标题】:How to delete all instances of a repeated number in a list? [duplicate]如何删除列表中重复数字的所有实例? [复制]
【发布时间】:2021-01-06 20:11:37
【问题描述】:

我想要一个删除列表中重复的任何数字的所有实例的代码。

例如:

Inputlist = [2, 3, 6, 6, 8, 9, 12, 12, 14]
 
Outputlist = [2,3,8,9,14]

我已经尝试删除列表中的重复元素(通过使用“唯一”函数),但它仍然在列表中留下了该元素的单个实例!

seen = set()
uniq = []
for x in Outputlist:
    if x not in seen:
        uniq.append(x)
        seen.add(x)      
seen

我也浏览了很多 StackOverflow 文章,但它们的不同之处在于他们正在搜索从两个不同列表中删除公共元素,或者他们只想保留每个元素的一个实例。我想简单地删除所有常见的元素。

【问题讨论】:

  • 使用set(Inputlist)
  • 输出列表 = 列表(设置(输入列表))。将其保留为列表
  • @YossiLevi 和 goodvibration 请详细阅读预期的输入和输出,这将不起作用
  • 列表是否总是像示例中那样排序?
  • @VikrantSrivastava,当然,它可以预先排序,但在这种情况下,它将是 O(n*log2n) + O(n)。

标签: python


【解决方案1】:

您可以使用Counter

>>> from collections import Counter
>>> l = [2, 3, 6, 6, 8, 9, 12, 12, 14]
>>> res = [el for el, cnt in Counter(l).items() if cnt==1]
>>> res
[2, 3, 8, 9, 14]

【讨论】:

  • 这里的计数器有点矫枉过正,set 会完成这项工作。
  • 从输出列表中他想要只出现一次的元素,而不仅仅是删除重复项@mlisthenewcool
  • @Pac0 我怀疑是否有更有效的解决方案。计数器为 O(n)。无论如何,您都需要迭代整个列表。所以它不会比 O(n) 更好。
  • 我同意这是最干净和最 Pythonic 的解决方案。而且我敢肯定没有人关心这些微秒。
  • @Pac0:对于少量输入,O(n²) 成本不太可能引起注意。但渐近地,它肯定会输给O(n) 解决方案。为这么小的输入对各种O(n) 方法进行基准测试不太可能有效地证明可扩展性(考虑到初始设置成本通常超过每件商品的增量成本)。除非知道带有可接受的 big-O 的明显代码太慢,否则只使用明显代码。
【解决方案2】:

你总是可以有两套。一个检查seen 是否是唯一的。 set.discard(el) 如果存在将删除。

Inputlist = [2, 3, 6, 6, 8, 9, 12, 12, 14]

seen = set()
ans = set()

for el in Inputlist:
    if el not in seen:
        seen.add(el)
        ans.add(el)
    else:
        ans.discard(el)

print(list(ans))

编辑:为了傻笑,我测量了这两种解决方案的性能

from timeit import timeit


first = """
def get_from_two_sets():
    seen = set()
    ans = set()

    for el in (2, 3, 6, 6, 8, 9, 12, 12, 14):
        if el not in seen:
            seen.add(el)
            ans.add(el)
        else:
            ans.discard(el)"""


second = """

def get_from_counter():
    return [el for el, cnt in Counter((2, 3, 6, 6, 8, 9, 12, 12, 14)).items() if cnt == 1]
    """


print(timeit(stmt=first, number=10000000))
print(timeit(stmt=second, number=10000000, setup="from collections import Counter"))

产量

0.3130729760000577
0.46127468299982866

太好了!看来我的解决方案要快一些。不要浪费您节省的那几纳秒!

@abc 解决方案干净且 Pythonic,去吧。

【讨论】:

  • 请注意,由于使用了sets,此解决方案失去了排序(abc 的解决方案保留在现代 Python 上,其中dicts 是插入排序的)。您总是可以使用dictset 类似的方式为ans 保留顺序,但我同意,Counter 是显而易见的,Pythonic 并且足够高效(我怀疑它会缩小差距或由于Counter 的 C 加速计数功能,甚至可以在更大的输入上获胜),只需使用它。
【解决方案3】:

一个简单的列表推导就可以解决问题:

Inputlist = [2, 3, 6, 6, 8, 9, 12, 12, 14]
 
Outputlist = [item for item in Inputlist if Inputlist.count(item) == 1]

【讨论】:

  • 这是 O(n^2)。
  • 这是不必要的二次时间
  • 是的,但由于我们 1) 使用 python 和 2) OP 没有提到速度,我认为我们很好。此外,如果您有一个包含大量列表的大型项目,那么 SO 不应该是寻求帮助的地方
【解决方案4】:

仅应删除 连续 个重复项的情况的替代解决方案:

from itertools import groupby

inputlist = [2, 3, 6, 6, 8, 9, 12, 12, 14]

outputlist = [x for _, (x, *extra) in groupby(inputlist) if not extra]

所有这一切都是将相同值的运行组合在一起,将第一个副本解压缩到x,其余的输入list;我们检查 list 是否为空,以确定是否只有一个值或多个值,并且只保留它是单个值的那些。

如果您甚至不喜欢临时的extra list,使用the ilen solutions 中的一个而不是listify 组将允许类似的解决方案没有无限的临时存储:

outputlist = [x for x, grp in groupby(inputlist) if ilen(grp) == 1]

或者使用只检查“至少 2”而不迭代超过该点的助手:

def more_than_one(it):
    next(it)  # Assumes at least once, which is already the case with groupby groups
    try:
        next(it)
    except StopIteration:
        return True
    return False

outputlist = [x for x, grp in groupby(inputlist) if not more_than_one(grp)]

注意:我实际上通常更喜欢 abc's Counter-based solution,但如果您真的只想删除 相邻 个重复项,这还不够。

【讨论】:

    【解决方案5】:

    使用集合的另一种解决方案:将输入列表转换为集合,并从输入列表中删除该集合的所有元素。这只会在列表中留下重复项。现在将其转换为一组,您可以从另一组中减去一组。听起来很复杂,但非常简短且高效对于短名单

    l = [2, 3, 6, 6, 8, 9, 12, 12, 14]
    inset = set(l)
    
    for i in inset:   # <-- usually the element to remove is in the front,
        l.remove(i)   # <-- but in a worst case, this is slower than O(n)
    
    result = list(inset - set(l))
    

    简短示例列表的无关性能:

    # %timeit this solution
    1.18 µs ± 1.97 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
    # %timeit solution with seen-set
    1.23 µs ± 1.49 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
    # %timeit solution with Counter class
    2.76 µs ± 4.85 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
    

    对于包含 1000 个元素和 10% 重复的列表,Counter-solution 最快!

    【讨论】:

      【解决方案6】:

      如果输入已排序并且可以以最小值和最大值为界,这可以在 O(n) 中完成:

      min = -1
      max = 99999999  # put whatever you need
      J = [min] + I + [max]
      [y for (x,y,z) in zip(J, J[1:], J[2:]) if x < y and y < z]
      

      【讨论】:

        猜你喜欢
        • 2011-07-14
        • 2011-01-12
        • 1970-01-01
        • 1970-01-01
        • 2014-10-06
        • 1970-01-01
        • 2021-05-13
        • 2021-11-03
        • 2011-10-09
        相关资源
        最近更新 更多