我想使用Python的heapq
模块。但是,我需要跟踪每个值设置的索引。
所以我写了
class heap(list):
def __init__(self,xs):
super(heap,self).__init__(xs)
self._index_table = {x:i for i,x in enumerate(self)}
def __setitem__(self,i,v):
print(i,v)
super(heap,self).__setitem__(i,v)
self._index_table[v] = i
def append(self,x):
super(heap,self).append(x)
self._index_table[x] = len(self)-1
from heapq import heapify, heappush, heappop, _siftdown, _siftup
h = heap([4,3,2,1])
heapify(h)
heappush(h,12)
print(h)
print(h._index_table)
这打印
[1, 3, 2, 4, 12]
{1: 3, 2: 2, 3: 1, 4: 0}
heapify
和heappush
修改了我的列表中的条目,规避了我捕获所有作业的尝试。
为什么会这样?有没有解决的办法?还有一种方法可以使用heapq
模块并仍然跟踪每个值对应的索引吗?
编辑:
看起来我的代码中有一条heapify(h)
行,我在原帖中没有。解决了这个问题。
答案 0 :(得分:6)
在阅读heapq
源代码时,我注意到@ math4tots确实在导入C实现。所以我运行以下代码来证明它是否正在使用python源(它将调用list
中可重载的方法),或者它使用的C实现使用编译方法进行列表:
>>> class heap(list):
... def __init__(self,xs):
... super(heap,self).__init__(xs)
... self._index_table = {x:i for i,x in enumerate(self)}
...
... def __setitem__(self,i,v):
... print("SETITEM")
... print(i,v)
... super(heap,self).__setitem__(i,v)
... self._index_table[v] = i
...
... def append(self,x):
... print("APPEND")
... super(heap,self).append(x)
... self._index_table[x] = len(self)-1
...
>>>
>>>
>>> h = heap([4,3,2,1])
>>> heapify(h)
>>> h
[1, 3, 2, 4]
>>> h._index_table
{1: 3, 2: 2, 3: 1, 4: 0}
>>> heappush(h,42)
>>> h
[1, 3, 2, 4, 42]
>>> h._index_table
{1: 3, 2: 2, 3: 1, 4: 0}
它不打印单个字符串...这意味着它不使用我们正在查看的python源,但绝对是编译版本。
所以你的代码不太可能正常工作......
阅读heapq module
的C源代码证明我们正确:_siftup
函数使用PyList_SET_ITEM()
从列表中获取值,覆盖任何重载方法的尝试。
尽管如此,所有希望都没有丢失,进一步阅读C源代码,让我发现实际上C模块does not export _sitf*
函数实现了heapq算法。所以我看了下面的内容,作为一个双重检查:
>>> heapq.heapify
<built-in function heapify>
>>> heapq._siftup
<function _siftup at 0x10b36ab00>
这证明我是对的!
因此,您可以使用heapq模块中的heapify()
和heappush()
函数重新实现大约几行的_siftup()
和_siftdown()
函数没有被C代码遮蔽。
所以这里将重新实现它:
import heapq
class HeapQueue(list):
def __init__(self,xs):
super(HeapQueue,self).__init__(xs)
self._index_table = {x:i for i,x in enumerate(self)}
def __setitem__(self,i,v):
super(HeapQueue,self).__setitem__(i,v)
self._index_table[v] = i
def append(self,x):
super(HeapQueue,self).append(x)
self._index_table[x] = len(self)-1
def push(self, x):
self.append(x)
heapq._siftdown(self, 0, len(self)-1)
def heapify(self):
n = len(self)
for i in reversed(range(n//2)):
heapq._siftup(self, i)
结果:
>>> h = HeapQueue([4,3,2,1])
>>> print(h._index_table)
{1: 3, 2: 2, 3: 1, 4: 0}
>>> h.heapify()
>>> print(h)
[1, 3, 2, 4]
>>> print(h._index_table)
{1: 0, 2: 2, 3: 1, 4: 3}
>>> h.push(42)
>>> print(h._index_table)
{1: 0, 2: 2, 3: 1, 4: 3, 42: 4}
>>> print(h)
[1, 3, 2, 4, 42]
>>>
我的猜测是你不想保留heapify()
方法,而是把它作为构造函数的一部分,并为你自己的Heap Queue类想一个更好的界面。我只把它作为这个想法的概念证明。
HTH