我们说我有一个清单:
l = ['a', 'b', 'c', 'd', 'e', 'f', 'e']
如您所见,重复索引4和6。我的问题是:查看列表中是否有重复内容的最有效方法是什么?
选项1:
output = len(set(l)) != len(l):
如果输出为false,则不止一次存在一个值。
选项2:
output = True
for i in l:
if l.count(i) > 1:
output = False
如果输出为false,则不止一次存在一个值。
问题:
最有效的方法是什么?
如何计算这两个(或更多?)选项的O表示法?
谢谢!
答案 0 :(得分:6)
关于计算O()值:
选项1执行4项操作:创建一个集合,获取其长度,获取列表的长度,然后比较它们。其中,创建集合必须至少为O(n),其他集合最多为O,因此效率由集合创建主导。我相信Python中的集合的实现使得插入平均需要O(1),因此这应该是 O(n)。
选项2包含一个循环。在循环内部,您调用l.count
,它遍历整个列表以计算项目发生的次数。所以每次迭代都是O(n)。循环本身为循环中的每个项运行,因此n次。总效率 O(n * n)。
是否存在比选项1更快的内容取决于您的真实数据的特征,其长度,重复的可能性,不同项目的数量(如果它们都是小写字母,那么任何长度> 26的列表都有重复,检查真的很快)等等。它无法回答。但是O(n)真的很难被击败,如果重复很少,那么通常必须检查所有项目,而且必须已经是O(n)。
答案 1 :(得分:3)
循环并收集一组中看到的项目。
一旦找到第一个副本,请注意break
。在extrem(没有重复项)中,您将循环列表一次并构建一个包含每个列表项的集合。
l = ['a', 'b', 'c', 'd', 'e', 'f', 'e']
seen = set()
for x in l:
if x in seen:
print("seen '{}' already, done".format(x))
# As soon as find find the first duplicate, break.
break
seen.add(x)
输出:
seen 'e' already, done
答案 2 :(得分:2)
答案 3 :(得分:2)
这是我能想到的最简单的速度比较。
选项1基本上归结为从列表中创建一个集合:
(dev) go|c:\srv\tmp> python -m timeit "set(range(100))"
100000 loops, best of 3: 5.48 usec per loop
而选项2归结为遍历列表并进行成员资格测试(以及我跳过的集合添加):
(dev) go|c:\srv\tmp> python -m timeit "for i in range(100): i in set()"
10000 loops, best of 3: 22.1 usec per loop
(dev) go|c:\srv\tmp> python -m timeit "for i in range(50): i in set()"
100000 loops, best of 3: 10 usec per loop
(dev) go|c:\srv\tmp> python -m timeit "for i in range(25): i in set()"
100000 loops, best of 3: 5.2 usec per loop
所以,第一个副本必须 大多数 25%进入选项2的列表才能更快,并且添加第二个集合操作将进行比较选项2更糟糕。
答案 4 :(得分:0)
以精美的字典格式提供所有信息的解决方案如下:
from collections import Counter
l = ['a', 'b', 'c', 'd', 'e', 'f', 'e']
# Get how many of each exist.
counts = Counter(l)
# Looks like this:
# Counter({'e': 2, 'd': 1, 'c': 1, 'f': 1, 'a': 1, 'b': 1})
# Print letters which have 2 or more instances.
for i in counts:
if counts[i] > 1:
print(i, counts[i])
一旦你有了这个,就可以在不变的时间内访问各个字典元素。虽然打印每个副本仍然需要O(n)时间。这个,以及也是O(n)的计数器的初始成本使得O(2n)接近于O(n)。