【问题标题】:What is the time complexity of popping elements from list in Python?在 Python 中从列表中弹出元素的时间复杂度是多少?
【发布时间】:2008-10-12 15:58:59
【问题描述】:

我想知道 Python 中列表对象的 pop 方法的时间复杂度是多少(特别是在 CPython 中)。 list.pop(N) 的 N 值也会影响复杂性吗?

【问题讨论】:

    标签: python


    【解决方案1】:

    是的,弹出 Python 列表的 last 元素是 O(1),弹出一个 任意 元素是 O(N)(因为剩下的全部列表的一部分必须移动)。

    这是一篇关于如何存储和操作 Python 列表的精彩文章:http://effbot.org/zone/python-list.htm

    【讨论】:

    • 所以为了清楚起见,list.pop(0) 是 O(n) 而 list.pop() 是 O(1)。
    • 若要在 O(1) 中完成这两个操作,请使用 collections.deque 参见 here
    • @galath: 需要明确的是,deque 不是 O(1) 用于删除任意项目(您没有明确说明, 我只是想要以确保没有误解),因为它是块的双向链表(即数组),而不是单个元素。因此它通常仍然是 O(n)。删除双端队列中的第一项是 O(1),因为该块具有开始和结束索引,可以在不移动元素 s 的情况下对其进行操作。
    • @paxdiablo 抱歉没有更明确。 deque.pop 不接受参数 docs on deque,就像列表中的 pop 方法一样。它删除了最右边的元素。
    【解决方案2】:

    Pop() 最后一个元素应该是 O(1) 因为你只需要返回数组中最后一个元素引用的元素并更新最后一个元素的索引。我希望pop() 的任意元素为 O(N) 并且平均需要 N/2 次操作,因为您需要将任何元素移动到要在指针数组中向上删除一个位置的元素之外。

    【讨论】:

    • 我同意给出的答案,但解释是错误的。您可以在 O(1) 时间从列表中删除任何对象(本质上使 prev 指针指向下一个,然后删除它)问题是,为了在该位置找到对象,您需要遍历列表直到点(需要 O(N) 时间,不需要平均。)最后一个特殊情况说明:pop(0) 将需要 O(1),而不是 O(0)。
    • @ntg 列表是一个指针数组。要从中间的数组中删除一个指针,它后面的所有指针都必须在数组中向上移动,所花费的时间与列表的大小(我们表示为 N)成正比,因此为 O(N) .为了澄清 Big-O 表示法中的 N 不是要返回的元素的索引,而是一个以 O(1) 为常数时间限制算法运行时间的函数 - 即,它不依赖于名单。 O(N) 表示边界函数是列表大小的某个常数倍,f(n) = c*n + b。
    • 我的立场是正确的 :) 实际上,列表实现是一个指针数组。所以你的答案是正确的。答案中的 pop(N) 是指 pop(k),N,其中 k 是要删除的元素的位置,并且所述数组的大小是 N。然后 k 的范围可能从 0 到 N-1,因此平均 N /2 操作将元素 k+1,....,N-1 向后移动一个位置。
    • @ntg pop(0) 是 O(N),而不是 O(1)
    • @BillYang Appologies,如前所述,我的立场是正确的。我在想一个经典的链表实现(例如en.wikipedia.org/wiki/Linked_list)但是,python的列表似乎基本上是一个指针数组......
    【解决方案3】:

    L.pop(-1) 应该是 O(1),L.pop(0) 应该是 O(n)

    参见以下示例:

    from timeit import timeit
    if __name__ == "__main__":
        L = range(100000)
        print timeit("L.pop(0)",  setup="from __main__ import L", number=10000)
        L = range(100000)
        print timeit("L.pop(-1)", setup="from __main__ import L", number=10000)
    
    >>> 0.291752411828
    >>> 0.00161794329896
    

    【讨论】:

    • 为什么会这样? 'L.pop(0)' 和 L.pop(-1) 有什么区别
    【解决方案4】:

    简短的答案请看这里:https://wiki.python.org/moin/TimeComplexity

    没有参数来弹出它的 O(1)

    带参数pop:

    • 平均时间复杂度 O(k)(k 表示传入的数字为 流行的论据
    • 摊销的最坏情况时间复杂度 O(k)
    • 最坏情况时间复杂度 O(n)

    平均时间复杂度:

    • 任何时候输入一个值,该操作的时间复杂度都是 O(n - k)。

    • 例如,如果您有一个包含 9 个项目的列表,则从列表末尾删除是 9 个操作并从开头删除 该列表是 1 个操作(删除第 0 个索引并移动所有 其他元素的当前索引 - 1)

    • 由于列表中间元素的 n - k 是 k 次操作,因此平均值可以缩短到 O(k)。

    • 另一种思考方式是假设每个索引都从您的 9 项列表中删除了一次。那将是总共 45 次操作。 (9+8+7+6+5+4+3+2+1 = 45)

    • 45 等于 O(nk) 并且由于弹出操作发生了 O(n) 次,因此您将 nk 除以 n 得到 O(k)

    摊销的最坏情况时间复杂度

    • 假设您又有一个包含 9 个项目的列表。想象一下,您正在删除列表中的每一项,而最坏的情况发生了,您每次都删除了列表中的第一项。

    • 由于列表每次缩小 1,因此总操作数每次从 9 减少到 1。

    • 9 + 8 + 7 + 6 + 5 + 4 + 3 + 2 + 1 = 45。45 等于 O(nk)。由于您进行了 9 次操作,并且 9 是 O(n) 来计算摊销的最坏情况,因此您执行 O(nk) / O(n) 等于 O(k)

    • 说明平均和摊销的最坏情况时间复杂度为 O(n) 也是正确的。请注意,O(k) 大约为 O(1/2n),删除常数等于 O(n)

    最坏情况时间复杂度

    • 与摊销的最坏情况时间复杂度不同,您无需考虑数据结构的状态,只需考虑任何单个操作的最坏情况。
    • 在这种情况下,最坏的情况是您必须从列表中删除第一项,即 O(n) 时间。

    Here's what I wrote to think this through in case it helps:

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-06-13
      • 1970-01-01
      • 2016-08-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多