如何在O(1)时间内将列表中的最后一项移到前面?

时间:2017-09-12 19:46:30

标签: python python-3.x heap

我的代码如下所示,其中heaplist

 heap[0] = heap.pop()

问题是弹出唯一的项目时不起作用。根据python wiki,弹出最后一个元素是O(1)。 (我也想删除最后一个元素)

到目前为止,我提出了什么:

last_element = heap.pop()  # I don't want to catch potential IndexError here
try:
    heap[0] = last_element
except IndexError:
    heap.append(last_element)

这是更长的

或者

if len(heap) != 1:
    heap[0] = heap.pop()

应该更慢。

有更多的pythonic方法吗?内部python可能甚至没有调整数组的大小。

更新: 我需要O(1)访问给定indeces的元素(因此它不能是链表)。

4 个答案:

答案 0 :(得分:6)

听起来你正在实现一个堆,这一行是heap-pop操作实现的一部分。在上下文中,它看起来像

def heappop_wrong(heap):
    popped_value = heap[0]
    heap[0] = heap.pop()  # <-- here
    sift_down(heap, 0)
    return popped_value

在这种情况下,如果列表只有一个元素,则不应该将最后一个元素移到前面。您应该删除并返回唯一的元素:

def heappop(heap):
    if len(heap) == 1:
        return heap.pop()
    popped_value = heap[0]
    heap[0] = heap.pop()
    sift_down(heap, 0)
    return popped_value

至于if的费用,这笔费用是微不足道的,根本不是你应该担心的事情。

为了将列表的最后一个元素移动到前面而不用替换旧的前面元素,这在O(1)时间内是不可能的。您必须使用不同的数据结构,例如collections.deque,或者实现您自己的可调整大小的ring buffer。但是,这与堆用例无关。

如果将列表的最后一个元素移到前面,如果有多个元素则踩过旧的第一个元素,如果只有一个元素则保持列表不变,if检查可能是最清楚的,但是再次,“如果只有一个元素,则保持列表不变”行为对于实现堆弹出实际上没有用。

答案 1 :(得分:3)

一种有效的方法(无例外,只要list非空)就是分配给list的切片:

heap[:1] = (heap.pop(),)

弹出最后一个元素并创建一个长度1 tuple,然后将其分配给heap,以便它替换第一个元素(如果存在),或者成为{{1的内容如果它是空的(由于list)。它应该保持pop,因为它总是用单个元素替换单个元素;更高的指数没有受到影响。

答案 2 :(得分:0)

collections.deque支持在O(1)中删除和附加正面和背面。

请参阅https://wiki.python.org/moin/TimeComplexity

答案 3 :(得分:0)

您可以在尝试弹出之前检查列表是否为空。

Error: package or namespace load failed for ‘sf’ in dyn.load(file, DLLpath = DLLpath, ...):

默认情况下,pop返回列表中的最后一个元素,因此您不必显式传递-1。并且由于如果列表为空它会引发错误,我们可以在尝试弹出之前检查其长度。

但是列表上的插入操作是O(n)。对于O(1),您将需要使用出队。

if len(a) > 0:
  a.insert(0,a.pop())