运行 "in" 使用 Python 搜索 "list" 和 "set" 的时间差

Run time difference for "in" searching through "list" and "set" using Python

我对list和set的理解Python主要是list允许重复,list允许有序信息,list有位置信息。我发现当我尝试搜索某个元素是否在列表中时,如果我先将列表转换为集合,运行时间会快得多。例如,我写了一段代码试图找到列表中最长的连续序列。以从0到10000的列表为例,最长连续为10001。使用列表时:

    start_time = datetime.now()
    nums = list(range(10000))
    longest = 0
    for number in nums:
        if number - 1 not in nums:
            length = 0
            ##Search if number + 1 also in the list##
            while number + length in nums: 
                length += 1
            longest = max(length, longest)
    end_time = datetime.now()
    timecost = 'Duration: {}'.format(end_time - start_time)
    print(timecost)

上面代码的运行时间是“持续时间:0:00:01.481939” 仅添加一行以将列表转换为下面第三行中的设置:

    start_time = datetime.now()
    nums = list(range(10000))
    nums = set(nums)
    longest = 0
    for number in nums:
        if number - 1 not in nums:
            length = 0
            ##Search if number + 1 also in the set(was a list)##
            while number + length in nums:
                length += 1
            longest = max(length, longest)
    end_time = datetime.now()
    timecost = 'Duration: {}'.format(end_time - start_time)
    print(timecost)

上述代码使用集合的 运行 时间现在是“持续时间:0:00:00.005138”,比搜索列表的时间短很多。谁能帮我理解其中的原因?谢谢!

这是一个很好的问题。

数组的问题在于,除了逐一比较每个元素之外,没有更智能的方法来搜索某个数组 a

  • 有时您会很幸运并在 a 的第一个元素上找到匹配项。
  • 有时您会很不走运,直到 a 的最后一个元素才找到匹配项,或者可能 none 根本找不到匹配项。
  • 平均而言,您每次必须搜索 they 数组的一半元素。

据说这具有 O(len(a)) 的“时间复杂度”,或者通俗地说,O(n)。这意味着算法(在数组中搜索一个值)所花费的时间与输入的大小(要搜索的数组中的元素数)成线性关系。这就是为什么它被称为“线性搜索”。哦,你的数组变大了 2 倍?好吧,您的搜索速度变慢了 2 倍。大 1000 倍?慢 1000 倍。

数组很棒,但它们用于搜索元素数量是否过高。

集合很聪明。在 Python 中,它们的实现就像是带有键但没有值的字典。像字典一样,它们由称为 hash table.

的数据结构支持

散列table 使用值的散列作为快速获取对象“摘要”的方法。这个“摘要”然后用于缩小搜索范围,因此它只需要线性搜索所有元素的一个非常小的子集。在哈希 table 中搜索时间复杂度 O(1)。请注意,其中没有“n”或 len(the_set)。这是因为在散列 table 中搜索所花费的时间不会随着散列 table 大小的增长而增长。所以据说它具有常数时间复杂度

打个比方,当你在寻找牛奶时,你只会搜索奶制品岛。你知道牛奶(比如 isle)的哈希值是“dairy”而不是“deli”,所以你不必浪费任何时间搜索牛奶

一个自然的 follow-up 问题是“那我们为什么不总是使用集合呢?”。好吧,有一个 trade-off.

  • 正如您所提到的,集合不能包含重复项,因此如果您想存储两个内容,可以是 non-starter。
  • 在 Python 3.7 之前,它们也是无序的,所以如果你关心 元素的顺序,他们也不会这样做。 * 套装通常有一个 更大的 cpu/memory 开销,当使用许多包含少量元素的集合时,开销会增加。
  • 还有可能 因为奇特的 CPU 特性(比如 CPU 缓存和分支 预测),通过小数组进行线性搜索实际上可以 比 hash-based look-up 更快

我建议您进一步阅读数据结构和算法。这玩意相当language-independent。现在您知道 setdict 在幕后使用哈希 Table,您可以查找涵盖任何语言的哈希 table 的资源,这应该会有所帮助.还有一些 Python-centric 资源,比如 https://www.interviewcake.com/concept/python/hash-map