O(n)列表减法

时间:2017-01-06 21:56:08

标签: python bag

working on an AoC puzzle时,我发现我想减去列表(保留排序):

def bag_sub(list_big, sublist):
    result = list_big[:]
    for n in sublist:
        result.remove(n)
    return result

我不喜欢list.remove调用(它本身就是O(n))包含在循环中的方式,这似乎是不必要的低效率。所以我试着重写它以避免这种情况:

def bag_sub(list_big, sublist):
    c = Counter(sublist)
    result = []
    for k in list_big:
        if k in c:
            c -= Counter({k: 1})
        else:
            result.append(k)
    return result
  1. 现在这是O(n),还是Counter.__isub__用法仍然搞砸了?

  2. 这种方法要求元素必须是可清除的,这是原始文件没有的限制。是否有O(n)解决方案,避免产生这种额外的限制? Python有没有比collections.Counter更好的“bag”数据类型?

  3. 您可以假设sublist的长度是list_big的一半。

3 个答案:

答案 0 :(得分:3)

我使用了一个计数器,但我可能会略有不同,我可能会反复这样做......

def bag_sub(big_list, sublist):
    sublist_counts = Counter(sublist)
    result = []
    for item in big_list:
        if sublist_counts[item] > 0:
            sublist_counts[item] -= 1
        else:
            result.append(item)
    return result

这与你的解决方案非常相似,但每次你想减少某些东西的数量时,创建一个全新的计数器可能效率不高。 1

此外,如果您不需要返回列表,请考虑生成器函数...

只要list_bigsublist中的所有元素都可以进行哈希处理,就可以正常工作。此解决方案为O(N + M),其中NM分别是list_bigsublist的长度。

如果元素无法进行哈希处理,除非您有其他约束(例如,使用相同的标准对输入进行排序),否则运气不佳。如果您的输入已排序,您可以执行类似于合并排序的合并阶段的操作,以确定bag_subsublist中的哪些元素。{/ p>

1 请注意,Counter的行为与defaultdict(int)的行为非常相似,因此在柜台中查找项目时完全没问题已经没有了。

答案 1 :(得分:2)

  

这现在是O(n),还是Counter.__isub__用法仍然搞砸了?

这是预期的情况O(n),除了当Counter.__isub__丢弃非正值时,它会通过每个键来执行此操作。你最好只从键值减去1"通常"方式并检查c[k]而不是k in cc[k]的{​​{1}}为0,因此您不需要进行k not in c检查。)

in
  

是否有O(n)解决方案可避免产生此额外限制?

仅当输入已排序时,在这种情况下,mergesort merge的标准变体才能执行此操作。

  

Python有没有更好的" bag"数据类型比if c[k]: c[k] -= 1 else: result.append(k)

collections.Counter是Python的包。

答案 2 :(得分:-1)

  1. 如果列表无序,则从长度为N的列表中删除项目为O(N),因为您必须找到它。
  2. 如果我们专注于&#34;合理的&#34;那么,从长度为N的列表中删除k项是O(kN)。 k <&lt;&lt; Ñ
  3. 所以我不知道如何把它降到O(N)。

    写这篇文章的简明方法:

    new_list = [x for x in list_big if x not in sublist]
    

    但那仍然是O(kN)。