Java';s PriorityQueue显示添加了更高优先级的重复密钥的意外行为

Java';s PriorityQueue显示添加了更高优先级的重复密钥的意外行为,java,heap,priority-queue,binary-heap,Java,Heap,Priority Queue,Binary Heap,我试图使用优先级队列的PriorityQueue类实现Djikstra算法的一个稍加修改的版本。我遇到了一个奇怪的行为,我在任何地方都找不到解释。我对这种行为有怀疑,但想知道其他人怎么看。有人能解释为什么PriorityQueue的行为如下所示: 由于标准算法涉及在“relax”操作期间降低节点的权重,这意味着将句柄提取到节点并降低其权重(我知道使用hashmap备份队列的方法,但这是一个实验)。此方法只需将节点的另一个副本添加到PriorityQueue中。在节点处理一次后,重复项将被忽略。然

我试图使用优先级队列的PriorityQueue类实现Djikstra算法的一个稍加修改的版本。我遇到了一个奇怪的行为,我在任何地方都找不到解释。我对这种行为有怀疑,但想知道其他人怎么看。有人能解释为什么PriorityQueue的行为如下所示:

由于标准算法涉及在“relax”操作期间降低节点的权重,这意味着将句柄提取到节点并降低其权重(我知道使用hashmap备份队列的方法,但这是一个实验)。此方法只需将节点的另一个副本添加到PriorityQueue中。在节点处理一次后,重复项将被忽略。然而,这种实现的问题是,对于随后添加的重复项,PriorityQueue的行为方式是未定义的。下面是一个片段,显示了同样的情况:

import java.util.PriorityQueue;
public class PriorityQueueTest {
    public static void main(String[] args) {
        int[] weight = new int[]{1, 2, 3};

        PriorityQueue<Integer> pq = new PriorityQueue<>(weight.length, (a, b) -> weight[a] - weight[b]);        
        for(int i=0; i<3; i++)
            pq.add(i);

        //change weight of node '1' from 2 to 0
        weight[1] = 0;
        //add duplicate node '1'. Note that this time weight 0 by the comparator.
        pq.add(1);

        while(!pq.isEmpty())
            System.out.println(pq.remove());
    }    
}
但实际结果不同:

0
1
1
2
还要注意,如果我只处理节点0和1(将上面的for循环更改为

for(int i=0; i<2; i++)
与实际订单相同

您可以添加更多节点并更改其他节点的权重,这会再次导致这种意外行为,有时取决于第一次复制后添加的节点数


我的怀疑是,在添加副本时,会执行heapify操作,在此期间,旧密钥可能会破坏堆不变量(具有更高的优先级,但在权重更新后可能会被放错位置)而该旧节点上的任何后续堆可能会使堆处于不一致的状态。想法?????

问题在于,您正在更改权重,但没有对队列重新排序。在您的示例中,您添加了三个值为0、1和2的项。
PriorityQueue
在内部使用二进制堆。创建的堆是:

      1
    /   \
   2     3
然后将
2
节点的值更改为
0
,从而导致此无效堆:

      1
    /   \
   0     3
现在您有了一个无效的堆。在二进制最小堆中,每个节点都必须小于或等于其父节点。但在这种情况下,
1
0
的父节点。从这一点开始,对堆执行的任何操作都将产生不可预测的结果


Java
PriorityQueue
不适合更改节点优先级。它没有删除任意元素的方法,也不允许您访问备份存储,因此您无法编写自己的备份存储。如果您想要更改节点优先级,您必须找到支持它的优先级队列实现,or滚动你自己的。

想法?是的,你不应该更改堆内事物的优先级。这就像更改映射键的哈希代码一样,它会破坏内容。如果你想更改PQ中某个项目的优先级,你必须先删除它,然后再替换它。“添加了更高优先级的重复键”体现了一种矛盾术语。是的,我也有同样的怀疑。然而,令人沮丧的是,我找不到与我所寻找的解释相同的链接现有重复问题中的一个小要点。无论如何,感谢您的回答!
      1
    /   \
   2     3
      1
    /   \
   0     3