我编写了一段代码,其中列表大小随着每次迭代而增加,迭代次数可以达到近100000次。
示例:
def do_something():
Lst.append(k)
while N < 100000:
if k not in Lst:
do_something()
现在,我注意到这种方法需要很长时间才能完成。请注意,我确实设置了setrecursionlimit()。事实上令人尴尬的是,该计划一直在运行。
稍后,在尝试查找优化代码的方法时,我将Lst转换为Dct。所以代码看起来像:
def do_something():
Dct[k] = True
while N < 100000:
if Dct[k] == False:
do_something()
代码运行得更快。在SOF上阅读了几个对话(Repeatedly appending to a large list (Python 2.6.6))后,我意识到它的列表不是很慢,而是如何处理更大的列表数据的内存。这个网站https://wiki.python.org/moin/TimeComplexity揭示了列表和dict查找时间的复杂性。列表中的O(n),其中Dct查找为O(1)。这是Dct表现更好的原因吗?如何执行列表查找和Dict查找?
答案 0 :(得分:5)
是的,字典查找需要一段时间。在添加之前,您的if k not in Lst
可能必须扫描整个列表以查看该号码是否尚未列在列表中。正是这种扫描使列表包含测试花费了O(n)时间,并且正在杀死你的算法。
另一方面,python字典使用hash table来测试成员资格。每个键都经过哈希处理(缩减为一个数字),然后将数字转换为索引到表中。如果在该位置找到的密钥等于您正在测试的密钥,则会找到匹配项。散列可能导致冲突(两个值散列到同一个表索引),但Python字典实现有一个算法,然后以有效的方式查找下一个插槽。如果找到空槽,则包含测试失败,则该密钥不存在。
因此,要测试字典中是否有k
,对于大多数测试,只需要进行1次计算。对于一些人来说,可能还需要进行一些测试。但平均而言,查找时间是不变的。
如果您感到好奇并且对C足够了解,请查看C implementation以获取所有(详细记录的)详细信息。您还可以观看Pycon 2010 presentation by Brandon Rhodes关于CPython dict
的工作原理,或者获取Beautiful Code的副本,其中包括Andrew Kuchling撰写的有关实施的章节。
你想看看set()
type;这就像字典一样,是一个带有O(1)成员资格测试的无序集合,但只是值,没有键:
some_set = set()
def do_something():
some_set.add(k)
while N < 100000:
if k not in some_set:
do_something()
在内部,set()
对象也使用哈希表。