我正在开发一个演示 Djikstra算法的应用程序,并且要使用它,我需要在我的元素值减少时恢复堆属性。
关于复杂性的问题是当算法更改元素的值时,用于优先级队列的内部结构(在本例中为堆)中的元素索引未知即可。因此,我之前需要进行O(n)搜索,以便恢复索引,然后才能对其执行实际的 reduce-key 。
此外,我不确定操作所需的实际代码。我正在使用D-Heap here作为我的优先级队列。伪代码会有所帮助,但我更喜欢Java中的一个例子,说明应如何做到这一点。
答案 0 :(得分:27)
您可以执行以下操作:在堆中存储将堆值映射到堆索引的哈希映射。然后你应该扩展你常用的堆逻辑:
on Swap(i, j):
map[value[i]] = j;
map[value[j]] = i;
on Insert(key, value):
map.Add(value, heapSize) in the beginning;
on ExtractMin:
map.Remove(extractedValue) in the end;
on UpdateKey(value, newKey):
index = map[value];
keys[index] = newKey;
如果BubbleUp(index)
,则{p> DecreaseKey
,BubbleDown/Heapify(index)
时IncreaseKey
,以恢复min-heap-property。
这是我的C#实现:http://pastebin.com/kkZn123m
在恢复堆属性时,插入和ExtractMin调用Swap log(N)次。而你正在向Swap添加O(1)开销,因此两个操作都保持为O(log(n))。 UpdateKey也是log(N):首先在O(1)的hashmap中查找索引,然后像在Insert / ExtractMin中那样恢复O(log(N))的堆属性。
重要提示:使用索引查找值将要求它们是唯一的。如果您对此条件不满意,则必须向键值对添加一些唯一ID,并维护此唯一ID和堆索引之间的映射,而不是值索引映射。但对于Dijkstra而言,它不是必需的,因为您的值将是图形节点,并且您不希望优先级队列中有重复的节点。
答案 1 :(得分:16)
Per this SO question没有必要使用减少键方法来实现Dijkstra算法。
您可以根据需要将项目添加到优先级队列中,并跟踪您访问过的节点以清除重复项。第一次通过将节点弹出队列实际访问节点时,您已找到该节点的最短路径,并且可以忽略优先级队列上将来出现的所有节点。
在优先级队列上有许多额外节点并不是什么大问题,因为它是O(log N)
结构。 (对于100万件物品,您必须进行20次比较,对10亿件物品进行30次比较。)
编辑:稍后再回答这个问题,我对我的回答感到有些失望:除非你以后做一些特殊的操作,否则所有这些事情都必须从队列中消失。就像生活中的许多事情一样,它归结为你如何管理你的记忆以及与此相关的成本。但一般的观点仍然存在:即使可能需要,减少键也不是必要的。
答案 2 :(得分:0)
如果您正在使用c ++ stl make_heap()/ pop_heap()/ push_heap(),则无法在下划线堆向量中保留从节点id到索引的索引,我认为您应该实现自己的堆函数在Increase-Key / Decrease-key操作中实现O(logn)。
答案 3 :(得分:0)
我实现了同样的事情。在MinHeap类中,我添加了一个字典,用于访问O(1)中的项目。并按减少键,它会在O(logn)时间冒泡。
--- Logging error ---
ERROR:py4j.java_gateway:An error occurred while trying to connect to the Java server (127.0.0.1:63717)
Traceback (most recent call last):
File "C:\Users\USER\AppData\Roaming\Python\Python37\site-packages\IPython\core\interactiveshell.py", line 3296, in run_code
exec(code_obj, self.user_global_ns, self.user_ns)
File "<ipython-input-4-d7fa2183fb85>", line 1, in <module>