Java 中的 PriorityQueue 无法按预期工作。具有较高优先级的对象被插入到队列的末尾

Posted

技术标签:

【中文标题】Java 中的 PriorityQueue 无法按预期工作。具有较高优先级的对象被插入到队列的末尾【英文标题】:PriorityQueue in Java does not work as expected. Objects with higher priority are inserted at end of the queue 【发布时间】:2016-02-25 06:13:08 【问题描述】:
package algo5;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.PriorityQueue;

public class prim 

    public static void main(String[] args) 
        // TODO Auto-generated method stub
        List<node> g = new ArrayList<node>();

        for (int i = 0; i < 6; i++) 
            node n =new node(i);
            n.name = i;
            g.add(n);
        
        prim p = new prim();
        p.pushdata(g, 0, 1, 5);
        p.pushdata(g, 0, 2, 6);
        p.pushdata(g, 0, 3, 4);
        p.pushdata(g, 1, 2, 1);
        p.pushdata(g, 1, 3, 2);
        p.pushdata(g, 2, 3, 2);
        p.pushdata(g, 2, 4, 5);
        p.pushdata(g, 2, 5, 3);
        p.pushdata(g, 3, 5, 4);
        p.pushdata(g, 4, 5, 4);

        p.prim(g, g.get(0));

    

    public void pushdata(List<node> g, int a, int b, int c)
        g.get(a).neighbours.add(g.get(b));
        g.get(b).neighbours.add(g.get(a));
        if (!g.get(a).lenmap.containsKey(b)) 
            g.get(a).lenmap.put(b, c);
        
        if (!g.get(b).lenmap.containsKey(a)) 
            g.get(b).lenmap.put(a, c);
        
    


    public void prim(List<node> g, node s)
        int inf = 10000;
        for (node node : g) 
            node.cost = inf;
            node.prev = null;
            node.visited = false;
        

        s.cost = 0;

        PriorityQueue<node> myQ = new PriorityQueue<node>();

        myQ.addAll(g);

        List<node> res = new ArrayList<node>();

        node u = null;

        while (!myQ.isEmpty()) 
            u = myQ.poll();
            if (!u.visited) 
                u.visited = true;
                res.add(u);
                for (node n : u.neighbours) 
                    if (n.cost>u.lenmap.get(n.name)) 
                        n.cost = u.lenmap.get(n.name);
                        n.prev = u;
                        myQ.offer(n);
                    
                
            
        

        for (node node : res) 
            System.out.println(node.name);
        
    


class node implements Serializable, Comparable
    int name;
    int cost;
    node prev = null;
    boolean visited = false;
    LinkedList<node> neighbours;
    HashMap<Integer, Integer> lenmap;

    public node(int name)
        this.name = name;
        neighbours = new LinkedList<node>();
        lenmap = new HashMap<Integer, Integer>();
    

    public boolean equals(node b)
        if (b.name==this.name) 
            return true;
        
        return false;
    

    @Override
    public int compareTo(Object a) 
        // TODO Auto-generated method stub
        node b = (node)a;       
        return this.cost-b.cost;
    

在第 68 行,将邻居重新插入队列的 while 循环在循环的第一次迭代结束期间将名称为 3 的节点插入到队列的末尾。但由于我使用了优先级队列,我希望它被插入到队列的顶部或头部。但是在第二次迭代中,只要我轮询队列的头部,名称为 3 的节点就会移动到队列的顶部。是否有任何命令可以让优先队列重新排序? add/offer 方法应该可以做到这一点吧?

【问题讨论】:

是的,但是当我尝试添加一个具有更高优先级的元素(在上述情况下,成本更低)时,它不应该将该元素添加到队列的头部吗?因为它在内部 foreach 循环的前两次运行中工作,其中节点成本为 5 和 6,并且这些是根据可比较的属性插入的。但是在第三次运行中,当一个成本为 4 的元素被添加到队列中时,它会被插入到最后。 【参考方案1】:

JavaDoc for PriorityQueue 说

“这个队列的头部是相对于指定顺序的最小元素。”

所以听起来您的队列的行为符合设计。如果您想颠倒排序,只需在您的node 类'compareTo 方法中翻转条件即可。

【讨论】:

如果两个对象之间存在联系,我不会问这个问题。当我添加另一个具有更高优先级的元素时,它不会被添加到队列的头部。最少的元素被添加到队列的末尾。 @BreadBoard JavaDocs 中的注释与对象之间的联系无关。这意味着最低优先级的元素被添加到队列的头部。它本质上是它如何运作的最小堆。如果你想从头部获取最高优先级的元素,而不是最低优先级,你可以使用this answer中引用的Collections.reverseOrder() this answer 中也显示了该技术的用法,它将 Collections API 比较器传递给PriorityQueue 的构造函数。 我知道我们需要使用 Collections.reverseOrder() 来获取具有最高优先级的元素。但是,在我的问题中,我要问的是,为什么即使在我添加了另一个成本 = 4 的元素之后,成本 = 5 的元素仍位于队列的头部。当我使用 add 或 offer 方法时,可比较的应该将 cost=4 定位在 cost=5 上。当我添加 cost =6 元素并且它位于 cost=5 元素下方时,此方法有效。当我将 cost=6 添加到一个头部为 cost=5 的队列中时,它会被定位在它之后。但是当我将 cost=4 添加到相同的位置时,它会位于队列的末尾。 @BreadBoard 你如何确定元素的顺序?同样来自文档:"The Iterator provided in method iterator() is not guaranteed to traverse the elements of the priority queue in any particular order." 您需要重复调​​用poll 方法才能从队列中检索元素,它们将按 4、5、6 的顺序取出。

以上是关于Java 中的 PriorityQueue 无法按预期工作。具有较高优先级的对象被插入到队列的末尾的主要内容,如果未能解决你的问题,请参考以下文章

PriorityQueue 抛出类强制转换异常

试图覆盖 Java 中 PriorityQueue 的现有比较器

Java数据结构及算法实战系列011:数组实现的优先级队列PriorityQueue

Java数据结构及算法实战系列011:数组实现的优先级队列PriorityQueue

Java数据结构及算法实战系列011:数组实现的优先级队列PriorityQueue

java - 为啥在java中的poll方法之后PriorityQueue中的值会发生变化? [复制]