编辑元素时Java优先级队列重新排序

编辑元素时Java优先级队列重新排序,java,priority-queue,Java,Priority Queue,我试图实现Dijkstra的算法,使用优先级队列查找最短路径。在算法的每一步中,我从优先级队列中移除距离最短的顶点,然后更新优先级队列中每个相邻顶点的距离。现在我了解到,Java中的优先级队列在编辑其中的元素(决定排序的元素)时不会重新排序,因此我尝试通过插入和删除虚拟顶点来强制它重新排序。但这似乎不起作用,我一直在努力想办法 这是顶点对象和比较器的代码 class vertex { int v, d; public vertex(int num, int dis) {

我试图实现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);
    }
int[]距离=新的int[p];
比较器比较器=新的顶点比较器();
PriorityQueue=新的PriorityQueue(p,比较器);

对于(int i=0;i您必须删除并重新插入每个编辑的元素。(实际元素,而不是虚拟元素!)。因此,每次更新距离时,您都需要删除并添加受更改的元素影响的元素


据我所知,这不是Java独有的,而是运行在O(logn)上的每个优先级队列对于所有操作,都必须以这种方式工作。

问题在于,您更新的是
距离数组,而不是
队列中相应的条目。要更新队列中相应的对象,您需要删除然后添加。

正如您所发现的,每当添加元素时,优先级队列不会使用所有元素删除r。这样做成本太高(记住比较排序的n logn下限),而任何合理的优先级队列实现(包括
PriorityQueue
)都承诺在O(logn)中添加/删除节点

事实上,它根本不会对元素进行排序(这就是为什么它的迭代器不能保证按排序顺序迭代元素)

PriorityQueue
不提供api来通知它已更改的节点,因为这需要它提供有效的节点查找,而它的底层算法不支持这种查找。实现优先级队列非常复杂。这可能是阅读此内容的一个好起点。我不确定这样的实现不过,这会更快

一个简单的方法是删除并添加更改的节点。不要这样做,因为
remove()
需要O(n)。相反,在PriorityQueue中插入同一节点的另一个条目,并在轮询队列时忽略重复项,即执行以下操作:

PriorityQueue<Step> queue = new PriorityQueue();

void findShortestPath(Node start) {
    start.distance = 0;
    queue.addAll(start.steps());

    Step step;
    while ((step = queue.poll()) != null) {
        Node node = step.target;
        if (!node.reached) {
            node.reached = true;
            node.distance = step.distance;
            queue.addAll(node.steps());
        }
    }

}
PriorityQueue queue=new PriorityQueue();
void findShortestPath(节点开始){
起始距离=0;
queue.addAll(start.steps());
一步一步;
而((step=queue.poll())!=null){
Node=step.target;
如果(!node.reach){
node.reach=true;
node.distance=step.distance;
queue.addAll(node.steps());
}
}
}

编辑:不建议更改PQ中元素的优先级,因此需要插入
步骤
s,而不是
节点
s。

您可以避免更新队列中的项目,默认情况下只将每个节点标记为已访问=false,并在运行时向队列添加新项目

然后从队列中弹出一个节点,仅当它以前未被访问时才对其进行处理

Dijkstra的算法保证每个节点只访问一次,因此即使队列中有过时的节点,也永远不会真正处理它们

另外,如果将算法内部与图形数据结构分离,可能会更容易

public void dijkstra(Node source) throws Exception{
    PriorityQueue q = new PriorityQueue();
    source.work.distance = 0;
    q.add(new DijkstraHeapItem(source));

    while(!q.isEmpty()){
        Node n = ((DijkstraHeapItem)q.remove()).node;
        Work w = n.work;

        if(!w.visited){
            w.visited = true;

            Iterator<Edge> adiacents = n.getEdgesIterator();
            while(adiacents.hasNext()){
                Edge e = adiacents.next();
                if(e.weight<0) throw new Exception("Negative weight!!");
                Integer relaxed = e.weight + w.distance;

                Node t = e.to;
                if (t.work.previous == null || t.work.distance > relaxed){
                    t.work.distance = relaxed;
                    t.work.previous = n;
                    q.add(new DijkstraHeapItem(t));
                }
            }
        }
    }
}
public void dijkstra(节点源)引发异常{
PriorityQueue q=新的PriorityQueue();
source.work.distance=0;
q、 添加(新DijkstraHeapItem(来源));
而(!q.isEmpty()){
节点n=((DijkstraHeapItem)q.remove()).Node;
功w=n.功;
如果(!w.已访问){
w、 访问=真实;
迭代器adiacents=n.getEdgesIterator();
while(adiacents.hasNext()){
边e=0.1分。下一步();
如果(如重量放松){
t、 工作距离=放松;
t、 work.previous=n;
q、 增加(新的DijkstraHeapItem(t));
}
}
}
}
}

我通过将进程划分为时间段(时间调度器就可以了)并扩展本机优先级队列来解决这个问题。因此,我实现了一个notify方法,其中该方法的关键是以下代码:

// If queue has one or less elements, then it shouldn't need an ordering
// procedure
if (size() > 1)
{
    // holds the current size, as during this process the size will
    // be vary
    int tmpSize = size();
    for (int i = 1; i < tmpSize; i++)
    {
        add(poll());
    }
}
//若队列有一个或更少的元素,那个么它不需要排序
//程序
如果(大小()>1)
{
//保持当前大小,因为在此过程中,大小将
//变化
int tmpSize=size();
for(int i=1;i

我希望这会有所帮助。

Java的
PriorityQueue
的缺点是
remove(Object)
需要O(n)个时间,如果您想通过再次删除和添加元素来更新优先级,则需要O(n)个时间。这可以在O(log(n))个时间内完成然而,由于我无法通过谷歌找到一个有效的实现,我尝试使用
TreeSet
自己实现它(尽管是在Kotlin中,因为我更喜欢那种语言而不是Java)。 这似乎可行,应该有O(log(n))用于添加/更新/删除(更新通过
add
)完成):

//通过再次添加元素更新优先级(自动删除旧事件)
类DynamicPriorityQueue(isMaxQueue:Boolean=false){
私有类MyComparator(val队列:DynamicPriorityQueue,isMaxQueue:Boolean):比较器{
val符号=如果(isMaxQueue)-1其他1
覆盖乐趣比较(o1:A,o2:A):Int{
如果(o1==o2)
返回0
if(queue.priorities[o2]!!-queue.priorities[o1]!!<0)
返回标志
返回标志
// update priority by adding element again (old occurrence is removed automatically)
class DynamicPriorityQueue<T>(isMaxQueue: Boolean = false) {

    private class MyComparator<A>(val queue: DynamicPriorityQueue<A>, isMaxQueue: Boolean) : Comparator<A> {
        val sign = if (isMaxQueue) -1 else 1

        override fun compare(o1: A, o2: A): Int {
            if (o1 == o2)
                return 0
            if (queue.priorities[o2]!! - queue.priorities[o1]!! < 0)
                return sign
            return -sign
        }

    }

    private val priorities = HashMap<T, Double>()
    private val treeSet = TreeSet<T>(MyComparator(this, isMaxQueue))

    val size: Int
        get() = treeSet.size

    fun isEmpty() = (size == 0)

    fun add(newElement: T, priority: Double) {
        if (newElement in priorities)
            treeSet.remove(newElement)
        priorities[newElement] = priority
        treeSet.add(newElement)
    }

    fun remove(element: T) {
        treeSet.remove(element)
        priorities.remove(element)
    }

    fun getPriorityOf(element: T): Double {
        return priorities[element]!!
    }


    fun first(): T = treeSet.first()
    fun poll(): T {
        val res = treeSet.pollFirst()
        priorities.remove(res)
        return res
    }

    fun pollWithPriority(): Pair<T, Double> {
        val res = treeSet.pollFirst()
        val priority = priorities[res]!!
        priorities.remove(res)
        return Pair(res, priority)
    }

}