我将名为UNVISITED
的空列表转换为堆,例如:
UNVISITED = []
heapq.heappush(UNVISITED, (a.f, a))
我推送的对象a
,它是从类中实例化的,具有以下字段:
class UNVISITEDNode():
def __init__(self, value1, value2 , value3, value4, value5):
self.q = value1
self.h = value2
self.g = value3
self.f = value4
self.p = value5
在我的算法中,每当需要时,我会继续修改堆中已有对象的任何valueX
,如:
for i in range(len(UNVISITED)):
UNVISITED[i][1].q = newvalue1
UNVISITED[i][1].h = newvalue2
UNVISITED[i][1].g = newvalue3
UNVISITED[i][1].f = newvalue4
UNVISITED[i][1].p = newvalue5
因为(或者我认为,如果我错了请纠正我)修改值f
就像我现在所做的那样不会改变影响堆排序的值,我直接尝试修改{ {1}}(以上UNVISITED[i][0]
在创建堆时作为第二个参数的第二部分传递。)
[问题] - >然后我被告知这个值不允许修改:
a.f
我真的需要修改对象UNVISITED[i][0] = newvalue4
*Traceback (most recent call last):
File "/home/daniel/pycharm-2017.3.3/helpers/pydev/
_pydevd_bundle/pydevd_exec.py", line 3, in Exec
exec exp in global_vars, local_vars
File "<input>", line 1, in <module>
TypeError: 'tuple' object does not support item assignment
的值f
,这必须在每次需要时影响堆的排序,而不能通过{{1}执行此操作(显然)。有没有办法做到这一点或任何解决方法?
最终我将一个简单的手动堆定义为a
和UNVISITED[i][1].f = newvalue4
对象。
您可以使用heap = []
弹出堆中的第一个元素,并使用heap.append()
根据属性的值对其进行排序。
像这样,您可以更接近heap.pop()
的行为,并且您可以修改堆中为其排序的元素。
重要的是要说这比使用heap.sort(key=lambda x: x.f, reverse=True)
要慢得多。
尽管如此,由于其他可能的解决方法的详细信息,我将@Raymong Hettinger的答案标记为好的答案。
此外,@ Davis Yoshida提出了一个有效的观点,可能是堆,因为它的定义可能不是存储数据的最佳方式。
答案 0 :(得分:3)
通常的解决方案是将对象标记为无效并重新插入新值。弹出值时,只需忽略无效的条目。
只要没有大量无效条目,此技术就非常有效。失效步骤在constant time中运行,随后的弹出窗口在logarithmic time中运行。
调整一个或多个值后,运行heapify()函数以恢复堆不变量。
这使用保证在linear time中运行的公共函数。
另一种方法是使用list.index()在堆的列表中找到对象。更改值后,运行内部 _siftup()或 _siftdown()函数,具体取决于值是增加还是减少。
增加案例:
>>> from heapq import _siftup, _siftdown, heapify, heappop
>>> data = [10, 5, 18, 2, 37, 3, 8, 7, 19, 1]
>>> heapify(data)
>>> old, new = 8, 22 # increase the 8 to 22
>>> i = data.index(old)
>>> data[i] = new
>>> _siftup(data, i)
>>> [heappop(data) for i in range(len(data))]
[1, 2, 3, 5, 7, 10, 18, 19, 22, 37]
减少案例:
>>> data = [10, 5, 18, 2, 37, 3, 8, 7, 19, 1]
>>> heapify(data)
>>> old, new = 8, 4 # decrease the 8 to 4
>>> i = data.index(old)
>>> data[i] = new
>>> _siftdown(data, 0, i)
>>> [heappop(data) for i in range(len(data))]
[1, 2, 3, 4, 5, 7, 10, 18, 19, 37]
此技术使用线性时间列表索引和logarithmic time堆更新。它可能比重新加热技术使用更少的比较,但这并不完全令人满意,因为它使用非公共函数。
最后,您可以使用数据:
>>> data.sort()
这种技术可能比重新调整或直接堆调整进行更多的比较。它起作用的原因是&#34;如果数据被排序,那么它已经是一个堆&#34;。
在最坏的情况下,运行时间可以是O(n log n)
;但是,排序
实现应用Timsort算法,该算法对部分排序的输入非常有效。
答案 1 :(得分:1)
即使这个代码运行,我相信它不会做你想要的。 具体来说,如果你做了
tup = (a.f, a) # a.f = 7
然后可以执行
tup[0] = 3
您将tup
设置为(3, a)
,但a.f
仍为7。
您可以做的一件事是通过向__lt__
类添加UNVISITEDNode
(小于)方法来进行直接比较,如下所示:
class UNVISITEDNode:
...
def __lt__(self, other):
return self.f < other.f
然后,不要将元组放入heapq,而是直接将节点对象放入。
但是,如果修改不在堆根目录的对象,则不再保证堆有效,因此您需要重新调整UNVISITED
。