【问题标题】:Why heappop time complexity is O(logn) (not O(n)) in python?为什么在 python 中 heappop 时间复杂度是 O(logn) (不是 O(n))?
【发布时间】:2018-08-05 07:29:47
【问题描述】:

对于列表,heappop 会弹出最前面的元素。从列表的前面删除一个元素的时间复杂度为 O(n)。 我错过了什么吗?

【问题讨论】:

    标签: python heap


    【解决方案1】:

    heappop() 重新排列列表中的 log(n) 元素,这样就不必移动每个元素。

    这很容易看到:

    >>> from random import randrange
    >>> from heapq import heapify, heappop
    >>> h = [randrange(1000) for i in range(15)]
    >>> heapify(h)
    >>> h
    [80, 126, 248, 336, 335, 413, 595, 405, 470, 592, 540, 566, 484, 970, 963]
    >>> heappop(h)
    80
    >>> h
    [126, 335, 248, 336, 540, 413, 595, 405, 470, 592, 963, 566, 484, 970]
    >>> #       ^----^---------^----^----^----^----^---------^----^----^--- elements that didn't move
    

    请注意,弹出操作并没有移动大部分元素(例如 248 在 heappop 之前和之后的位置相同)。

    【讨论】:

      【解决方案2】:

      如果您在考虑 list.pop 的作用,该文档会有些误导。

      如果heap 是一个minheap,那么heap[0] 确实是最小的项。 Python 的list.pop 方法返回列表的last 元素,但heapq.heappop 返回堆的最小(第一个!)元素。但是,它通过弹出堆的最后一个元素(这是对列表的 O(1) 操作),将其与heap[0] 交换,将其冒泡(这是 O(log n)),然后将从heap[0] 移除的值返回给调用者。

      所以:list.pop 返回列表中的最后一项,即O(1)heapq.heappop 将第一项返回给您,但不是通过移动整个数组。

      【讨论】:

        【解决方案3】:

        堆弹出确实是O(logn) 复杂度。

        您缺少的是从堆中弹出 像“删除第一个元素并将所有元素左移一个”。有一种算法可以移动链表内的元素,弹出后不保证链表中剩余元素的顺序和之前一样。

        【讨论】:

        • 是的。这是对正在发生的事情的最清楚的解释。 heappop() 重新排列列表中的 log(n) 个元素。
        【解决方案4】:

        看看heapq.heappop的实现:https://svn.python.org/projects/python/trunk/Lib/heapq.py

        它不会从列表的前面删除一个元素,它只是返回它并用其他需要 O(logn) 的元素替换它,并通过删除最后一个元素来减少底层列表的长度。

        【讨论】:

        • 堆弹出从来都不是固定时间。
        • @liliscent,正确,我的句子可能措辞不佳,我的意思是“从基础列表中删除最后一个元素是恒定时间”。我会编辑。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2023-04-01
        • 1970-01-01
        • 2023-02-23
        • 2011-10-22
        • 1970-01-01
        • 2021-01-01
        • 1970-01-01
        相关资源
        最近更新 更多