【发布时间】:2018-08-05 07:29:47
【问题描述】:
对于列表,heappop 会弹出最前面的元素。从列表的前面删除一个元素的时间复杂度为 O(n)。 我错过了什么吗?
【问题讨论】:
对于列表,heappop 会弹出最前面的元素。从列表的前面删除一个元素的时间复杂度为 O(n)。 我错过了什么吗?
【问题讨论】:
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 之前和之后的位置相同)。
【讨论】:
如果您在考虑 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 将第一项返回给您,但不是通过移动整个数组。
【讨论】:
堆弹出确实是O(logn) 复杂度。
您缺少的是从堆中弹出 不 像“删除第一个元素并将所有元素左移一个”。有一种算法可以移动链表内的元素,弹出后不保证链表中剩余元素的顺序和之前一样。
【讨论】:
看看heapq.heappop的实现:https://svn.python.org/projects/python/trunk/Lib/heapq.py
它不会从列表的前面删除一个元素,它只是返回它并用其他需要 O(logn) 的元素替换它,并通过删除最后一个元素来减少底层列表的长度。
【讨论】: