编辑元素时 Java 优先级队列重新排序
我正在尝试实现 Dijkstra 算法,以使用优先级队列查找最短路径。在算法的每一步中,我都会从优先级队列中删除距离最短的顶点,然后更新优先级队列中其每个邻居的距离。现在我读到,当您编辑 Java 中的优先级队列中的元素(确定顺序的元素)时,它不会重新排序,因此我尝试通过插入和删除虚拟顶点来强制它重新排序。但这似乎不起作用,我一直在试图弄清楚。
这是顶点对象和比较器的代码,
class vertex {
int v, d;
public vertex(int num, int dis) {
v=num;
d=dis;
}
}
class VertexComparator implements Comparator {
public int compare (Object a, Object b) {
vertex v1 = (vertex)a;
vertex v2 = (vertex)b;
return v1.d-v2.d;
}
}
这是我运行算法的地方:
int[] distances=new int[p];
Comparator<vertex> comparator = new VertexComparator();
PriorityQueue<vertex> queue = new PriorityQueue<vertex>(p, comparator);
for(int i=0; i<p; i++) {
if(i!=v) {
distances[i]=MAX;
}
else {
distances[i]=0;
}
queue.add(new vertex(i, distances[i]));
}
// run dijkstra
for(int i=0; i<p; i++) {
vertex cur=queue.poll();
Iterator itr = queue.iterator();
while(itr.hasNext()) {
vertex test = (vertex)(itr.next());
if(graph[cur.v][test.v]!=-1) {
test.d=Math.min(test.d, cur.d+graph[cur.v][test.v]);
distances[test.v]=test.d;
}
}
// force the PQ to resort by adding and then removing a dummy vertex
vertex resort = new vertex(-1, -1);
queue.add(resort);
queue.remove(resort);
}
我已经运行了几个文本案例,并且我知道每次我遍历并更新距离时,优先级队列都没有正确重新排序顶点,但我不知道为什么。我在某个地方犯了错误吗?
I'm trying to implement Dijkstra's algorithm for finding shortest paths using a priority queue. In each step of the algorithm, I remove the vertex with the shortest distance from the priority queue, and then update the distances for each of its neighbors in the priority queue. Now I read that a Priority Queue in Java won't reorder when you edit the elements in it (the elements that determine the ordering), so I tried to force it to reorder by inserting and removing a dummy vertex. But this doesn't seem to be working, and I'm stuck trying to figure it out.
This is the code for the vertex object and the comparator
class vertex {
int v, d;
public vertex(int num, int dis) {
v=num;
d=dis;
}
}
class VertexComparator implements Comparator {
public int compare (Object a, Object b) {
vertex v1 = (vertex)a;
vertex v2 = (vertex)b;
return v1.d-v2.d;
}
}
Here is then where I run the algorithm:
int[] distances=new int[p];
Comparator<vertex> comparator = new VertexComparator();
PriorityQueue<vertex> queue = new PriorityQueue<vertex>(p, comparator);
for(int i=0; i<p; i++) {
if(i!=v) {
distances[i]=MAX;
}
else {
distances[i]=0;
}
queue.add(new vertex(i, distances[i]));
}
// run dijkstra
for(int i=0; i<p; i++) {
vertex cur=queue.poll();
Iterator itr = queue.iterator();
while(itr.hasNext()) {
vertex test = (vertex)(itr.next());
if(graph[cur.v][test.v]!=-1) {
test.d=Math.min(test.d, cur.d+graph[cur.v][test.v]);
distances[test.v]=test.d;
}
}
// force the PQ to resort by adding and then removing a dummy vertex
vertex resort = new vertex(-1, -1);
queue.add(resort);
queue.remove(resort);
}
I've run several text cases, and I know that the priority queue isn't reordering correctly each time I go through and update the distances for vertices, but I don't know why. Did I make an error somewhere?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(7)
正如您所发现的,每当添加或删除元素时,优先级队列不会重新排序所有元素。这样做的成本太高(记住比较排序的 n log n 下限),而任何合理的优先级队列实现(包括
PriorityQueue
)都承诺在 O(log n) 中添加/删除节点。事实上,它根本不对其元素进行排序(这就是为什么它的迭代器不能承诺按排序顺序迭代元素)。
PriorityQueue
不提供 API 来通知它有关更改的节点,因为这需要它提供高效的节点查找,而其底层算法不支持这一点。实现一个优先级队列是相当复杂的。 关于 PriorityQueues 的维基百科文章可能是阅读相关内容的一个很好的起点。不过,我不确定这样的实现会更快。一个简单的想法是删除然后添加更改的节点。不要这样做,因为
remove()
需要 O(n)。相反,将同一节点的另一个条目插入到 PriorityQueue 中,并在轮询队列时忽略重复项,即执行以下操作:编辑:不建议更改 PQ 中元素的优先级,因此需要插入
步骤而不是节点。
As you discovered, a priority queue does not resort all elements whenever an element is added or removed. Doing that would be too expensive (remember the n log n lower bound for comparison sort), while any reasonable priority queue implementation (including
PriorityQueue
) promises to add/remove nodes in O(log n).In fact, it doesn't sort its elements at all (that's why its iterator can not promise to iterate elements in sorted order).
PriorityQueue
does not offer an api to inform it about a changed node, as that would require it to provide efficient node lookup, which its underlying algorithm does not support. Implementing a priority queue that does is quite involved. The Wikipedia article on PriorityQueues might be a good starting point for reading about that. I am not certain such an implementation would be faster, though.A straightforward idea is to remove and then add the changed node. Do not do that as
remove()
takes O(n). Instead, insert another entry for the same node into the PriorityQueue, and ignore duplicates when polling the queue, i.e. do something like:Edit: It is not advisable to change the priorities of elements in the PQ, hence the need to insert
Step
s instead ofNode
s.您必须删除并重新插入每个已编辑的元素。 (实际元素,而不是虚拟元素!)。因此,每次更新
distances
时,您都需要删除并添加受更改主菜影响的元素。据我所知,这并不是 Java 所独有的,但是每个以 O(logn) 运行所有操作的优先级队列都必须以这种方式工作。
you will have to delete and re-insert each element which is editted. (the actual element, and not a dummy one!). so, every time you update
distances
, you need to remove and add the elements that were affected by the changed entree.as far as I know, this is not unique to Java, but every priority queue which runs at O(logn) for all ops, have to work this way.
Java 的
PriorityQueue
的缺点是remove(Object)
需要 O(n) 时间,如果要通过删除和添加元素来更新优先级,则需要 O(n) 时间再次。然而,它可以在 O(log(n)) 时间内完成。由于我无法通过 Google 找到有效的实现,因此我尝试使用TreeSet
自己实现它(不过在 Kotlin 中,因为我真的更喜欢该语言而不是 Java)。这似乎可行,并且应该有 O(log(n)) 来添加/更新/删除(更新是通过
add
完成的):The disadvantage of Java's
PriorityQueue
is thatremove(Object)
requires O(n) time, resulting in O(n) time if you want to update priorities by removing and adding elements again. It can be done in time O(log(n)) however. As I wasn't able to find a working implementation via Google, I tried implementing it myself (in Kotlin though, as I really prefer that language to Java) usingTreeSet
.This seems to work, and should have O(log(n)) for adding/updating/removing (updating is done via
add
):您可以避免更新队列中的项目,只需将每个节点默认标记为 visited=false,然后随时将新项目添加到队列中。
然后从队列中弹出一个节点,只有在之前没有被访问过的情况下才处理它。
Dijkstra 的算法保证每个节点仅被访问一次,因此即使队列中可能有陈旧的节点,您也永远不会真正处理它们。
此外,如果将算法内部结构与图形数据结构分开,可能会更容易。
You can avoid updating items in the queue just marking each node as visited=false by default, and adding new items to the queue as you go.
Then pop a node from the queue and process it only if it was not visited before.
Dijkstra's algorithm guarantees that each node is visited only once, so even if you may have stale nodes down the queue you never really process them.
Also it's probably easier if you separate the algorithm internals from the graph data structure.
问题是您更新了
distances
数组,但没有更新queue
中的相应条目。要更新队列中适当的对象,您需要先删除然后再添加。The problem is that you update the
distances
array, but not the corresponding entry in thequeue
. To update the appropriate objects in the queue, you need to remove and then add.我通过将进程划分为时间槽(时间调度程序就可以了)并扩展本机优先级队列来解决这个问题。所以我实现了一个通知方法,该方法的关键是以下代码:
我希望它有所帮助。
I solve this problem by dividing my process into timeSlots ( A time Scheduler will be just fine ) and Extending the native PriorityQueue. So I implement a notify method where the key of this method is the following code:
I hope It helped.
我实现了一个自适应 MinHeap,它支持在更新对象优先级时重新排序 (O(nlogn)),用 Python 编写。
I implemented an adaptive MinHeap that supports reorderining (O(nlogn)) when objects' priority are updated, written in Python.