优先队列 - 在其背后更新密钥的效果

Priority Queue - Effect of updating keys behind its back

我试图理解为什么我的 A* 搜索实现似乎工作正常,即使我似乎正在更新优先队列背后的密钥。

在代表地图的 class 中,我有以下数据结构来保存地图中的所有节点(比如从文件加载)。

// maps a latitude/longitude to a node in the map
HashMap<GeographicPoint, MapNode> nodes;

为了实现 A* 搜索,我的 MapNode class 拥有 "distance from start" 和 "heuristic distance from goal" 属性。在搜索开始之前,我将地图中每个节点的距离初始化为无穷大。这一切都很好。

调用 StarSearch 函数时,我创建了一个优先队列 (PQ),如下所示:

PriorityQueue<MapNode> toExplore = new PriorityQueue<MapNode>();

现在,当我按如下方式将节点排入此 PQ 时:

toExplore.add(startNode);

注意,我没有创建节点的新副本。我只是为原始节点创建一个额外的 reference 并将其添加到 PQ。

稍后,作为 A* 实现的一部分,当我重新计算和更新节点对象中的距离时,我再次使用指向同一原始节点的引用来执行此操作。好吧,PQ 也引用了同一个原始节点,所以效果是我只是改变了 PQ 下的距离(即键)!

这对 PQ 不利。但一切仍然有效! -- 这意味着我得到了正确的最短路径并探索了正确数量的节点等。

提供 PQ 正在使用的 MapNode 实现的相关部分可能会有用:

public class MapNode implements Comparable<MapNode> {

// the location of the intersection in the world
private GeographicPoint location;


// NOTE: Equals method is based on comparing actual geographic point. 
// Whereas compareTo based on distances. 
// This implies a.equals(b) and a.compareTo(b) == 0 will return different result. 
// Is this OK? 

@Override
public boolean equals(Object obj) { 
    return this.location.equals(obj);
} 

// NOTE: Equals method is based on comparing actual geographic point. 
// Whereas compareTo based on distances. 
// This implies a.equals(b) and a.compareTo(b) == 0 will return different result. 
// Is this OK? 

@Override
public int compareTo(MapNode other) {
    // Comparison based on priorities
    return Double.compare(this.getTotalEstimatedDistance(), 
                          other.getTotalEstimatedDistance());
}

问题:

  1. 我不明白优先级队列如何在我出列时为我提供正确的最高优先级节点。我正在弄乱它背后的钥匙。
  2. 我怎样才能更好地设计它,使我没有这种代码味道?

如果需要,我可以提供额外的代码片段,以便更好地理解。

I don't understand how the Priority Queue would be able to give me the correct highest priority node when I dequeue. I am messing with it's keys behind it's back.

虽然这是个坏主意,但不能保证它会显示为问题。 PriorityQueue 未对所有条目(仅第一个)进行排序,因此更改另一个条目不一定是问题。在删除它之前尝试更改第一个。 ;)

How can I design this better such that I do not have this code smell?

每当您更改有序集合中的元素(以可能改变其位置的方式)时,您必须将其删除、更改并重新添加,以确保集合未损坏。

Queue<MapNode> q = new PriorityQueue<>(
                               Comparator.comparing(MapNode::getTotalEstimatedDistance));