简体   繁体   English

优先级队列插入键值对Java

[英]priority queue insert key value pair java

Background 背景

I am trying to code Dijkstra's algorithm in O(mlogn) time, where m is the number of edges and n is the number of nodes. 我正在尝试在O(mlogn)时间内编写Dijkstra的算法,其中m是边的数量,n是节点的数量。 I am using to find the shortest path between a given starting node and a given ending node. 我用来查找给定起始节点和给定终止节点之间的最短路径。 And I'm pretty new at this. 我对此很陌生。

Here is the algorithm I have come up with: 这是我想出的算法:

Assume the graph is represented by an adjacency matrix and each node has a row index. 假设该图由邻接矩阵表示,并且每个节点都有一个行索引。

Initialize starting node distance to zero, and all other nodes to inifinity, in the heap.

Create a list of shortest paths, equal to the number of nodes in the graph, set to 0.

While the index of the node that corresponds to the minimum element in the heap 
has no value in the list of shortest paths and heap has node distances, do:
    Remove the minimum node distance from the heap, and bubble as necessary to fill the removed node.
    Put the minimum node distance into the list of shortest paths at its row index.

    For all nodes that were adjacent to the node with the minimum distance (that was just removed), do:
      Update the distances in the heap for the current node, using the following calculation:
        min((deleted node distance + adjacent edge weight), current node's distance)
    Reorganize the heap to be a minimum heap.

Return value in the list of shortest paths at the location of the end node.

This is O(mlogn) because you only update the distances once per edge. 这是O(mlogn),因为每个边缘仅更新一次距离。

"It takes linear time to initialize the heap, and then we perform m updates at a cost of O(log n) each for a total time of O(mlog n)." “初始化堆需要线性时间,然后我们以O(log n)为代价执行m个更新,总共耗时O(mlog n)。” - http://www.cs.cmu.edu/~avrim/451f07/lectures/lect1011.pdf -http://www.cs.cmu.edu/~avrim/451f07/lectures/lect1011.pdf

Problem 问题

In order to update the distances from the starting vertex in the correct location in the heap, insertions to the heap must be key-value pairs - with the key being the node (row index) and the value being the distance. 为了更新到堆中正确位置的起始顶点的距离,插入到堆中的必须是键值对-键为节点(行索引),值为距离。

There are lecture slides online that say each entry in a priority queue ADT is a key-value pair (otherwise, how could it prioritize?). 在线上有一些幻灯片显示,优先级队列ADT中的每个条目都是一个键值对(否则,如何确定优先级?)。

Question

The methods for PriorityQueue have at most one parameter, so how do you insert a key associated with a value? PriorityQueue的方法最多具有一个参数,那么如何插入与值关联的键?

This must be done in a single file with a specific name (ie It is my understanding that I can't make a KeyValuePair class implementing Comparator ). 这必须在具有特定名称的单个文件中完成(即,据我了解,我无法制作实现ComparatorKeyValuePair类)。

I'd love to hear your thoughts. 我很想听听您的想法。

To use JDK's implementation of priority queue for your application, you can maintain a Map<Key, Value> in addition to PriorityQueue<Value> . 要为您的应用程序使用JDK的优先级队列实现,除了PriorityQueue<Value>之外,您还可以维护Map<Key, Value> PriorityQueue<Value> In your case, Key represents a node and Value is an object that holds the shortest distance to a node. 在您的情况下,“ Key代表一个节点,“ Value是一个与节点保持最短距离的对象。 To update the distance to a node, you first look up its corresponding distance object in the map. 要更新到节点的距离,您首先要在地图中查找其对应的距离对象。 Then, you remove the distance object from the priority queue. 然后,您从优先级队列中删除距离对象。 Next, you update the distance object. 接下来,更新距离对象。 Finally, you insert the distance object back in the priority queue. 最后,将距离对象重新插入优先级队列。

Below is the Dijkstra implementation using priority_queue . 以下是使用priority_queue的Dijkstra实现。 Here ignore the InputReader class as it is for fast input . 这里忽略InputReader类,因为它是用于快速输入的。 We can maintain priority according to "Value" of pair in key value pair . 我们可以根据键值对中的“值”来维持优先级。 Then choose the Pair with minimum cost ie value . 然后选择成本最低的货币对,即value。

import java.io.File;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.InputMismatchException;
import java.util.List;
import java.util.PriorityQueue;
/** 
 * By: Rajan Parmar
 * At : HackerRank 
 **/
public class Dijkstra { 
     // node ,pair ( neighbor , cost)
    static HashMap < Integer , HashSet <Pair>> node;
    static PrintWriter w;   
    public static void main(String [] s) throws Exception{
        InputReader in;

        boolean online = false;
        String fileName = "input";

        node = new HashMap<Integer, HashSet<Pair>>();


        //ignore online if false it is for online competition
        if (online) {

            //ignore
            in = new InputReader(new FileInputStream(
                    new File(fileName + ".txt")));
            w = new PrintWriter(new FileWriter(fileName + "Output.txt"));
        } else {

            // for fast input output . You can use any input method
            in = new InputReader(System.in);
            w = new PrintWriter(System.out);
        }

        // Actual code starts here
        int t;
        int n, m;
        t = in.nextInt();

        while(t-- > 0){
            n = in.nextInt();
            m = in.nextInt();
            while(m-- > 0){
                int x,y,cost;
                x = in.nextInt();
                y = in.nextInt();
                cost = in.nextInt();

                if(node.get(x)==null){
                    node.put(x, new HashSet());
                    node.get(x).add(new Pair(y,cost));
                }
                else{
                    node.get(x).add(new Pair(y,cost));
                }
                if(node.get(y)==null){
                    node.put(y, new HashSet());
                    node.get(y).add(new Pair(x,cost));
                }
                else{
                    node.get(y).add(new Pair(x,cost));
                }
            }
            int source = in.nextInt();
            Dijkstra(source,n);
            node.clear();
            System.out.println("");
        }
    }

    static void Dijkstra(int start , int n) {

        int dist[] = new int[3001];
        int visited[] = new int[3001];
        Arrays.fill(dist, Integer.MAX_VALUE);
        Arrays.fill(visited, 0);
        dist[start] = 0 ;
        PriorityQueue < Pair > pq = new PriorityQueue();

        //this will be prioritized according to VALUES (i.e cost in class Pair)
        pq.add(new Pair(start , 0));
        while(!pq.isEmpty()){
            Pair pr = pq.remove();
            visited[pr.neighbor] = 1;
            for(Pair p:node.get(pr.neighbor)){
                if(dist[p.neighbor] > dist[pr.neighbor] + p.cost){
                    dist[p.neighbor] = dist[pr.neighbor] + p.cost;

                    //add updates cost to vertex through start vertex
                    if(visited[p.neighbor]==0)
                        pq.add(new Pair(p.neighbor ,dist[p.neighbor] ));
                }

            }
        }
        for(int i=1;i<=n;i++){
            if(i==start) continue;
            if(visited[i]==0)
                dist[i]=-1;
            System.out.print(dist[i]+" ");
        }
    }

    static class Pair implements Comparable {

        int neighbor;
        int cost;

        public Pair(int y, int cost) {
            // TODO Auto-generated constructor stub
            neighbor = y;
            this.cost = cost;
        }

        @Override
        public int compareTo(Object o) {
            // TODO Auto-generated method stub
            Pair pr = (Pair)o;

            if(cost > pr.cost)
                return 1;
            else
                return -1;

        }

    }

    //Ignore this class , it is for fast input.
    static class InputReader {

        private InputStream stream;
        private byte[] buf = new byte[8192];
        private int curChar, snumChars;
        private SpaceCharFilter filter;

        public InputReader(InputStream stream) {
            this.stream = stream;
        }

        public int snext() {
            if (snumChars == -1)
                throw new InputMismatchException();
            if (curChar >= snumChars) {
                curChar = 0;
                try {
                    snumChars = stream.read(buf);
                } catch (IOException e) {
                    throw new InputMismatchException();
                }
                if (snumChars <= 0)
                    return -1;
            }
            return buf[curChar++];
        }

        public int nextInt() {
            int c = snext();
            while (isSpaceChar(c))
                c = snext();
            int sgn = 1;
            if (c == '-') {
                sgn = -1;
                c = snext();
            }
            int res = 0;
            do {
                if (c < '0' || c > '9')
                    throw new InputMismatchException();
                res *= 10;
                res += c - '0';
                c = snext();
            } while (!isSpaceChar(c));
            return res * sgn;
        }

        public long nextLong() {
            int c = snext();
            while (isSpaceChar(c))
                c = snext();
            int sgn = 1;
            if (c == '-') {
                sgn = -1;
                c = snext();
            }
            long res = 0;
            do {
                if (c < '0' || c > '9')
                    throw new InputMismatchException();
                res *= 10;
                res += c - '0';
                c = snext();
            } while (!isSpaceChar(c));
            return res * sgn;
        }

        public int[] nextIntArray(int n) {
            int a[] = new int[n];
            for (int i = 0; i < n; i++)
                a[i] = nextInt();
            return a;
        }

        public String readString() {
            int c = snext();
            while (isSpaceChar(c))
                c = snext();
            StringBuilder res = new StringBuilder();
            do {
                res.appendCodePoint(c);
                c = snext();
            } while (!isSpaceChar(c));
            return res.toString();
        }

        public boolean isSpaceChar(int c) {
            if (filter != null)
                return filter.isSpaceChar(c);
            return c == ' ' || c == '\n' || c == '\r' || c == '\t' || c == -1;
        }

        public interface SpaceCharFilter {
            public boolean isSpaceChar(int ch);
        }
    }
}

This will take input in following format . 这将采用以下格式的输入。

First line be T (no. of test case). 第一行是T(测试用例的编号)。

For each test case next line input will be N and M , where N is no of nodes , M is no of edges. 对于每个测试用例,下一行输入将为N和M,其中N为节点数,M为边数。

Next M line contains 3 integers ie x,y,W. 接下来的M行包含3个整数,即x,y,W。 It represents edge between node x and y with weight W. 它表示权重为W的节点x和y之间的边缘。

Next line contain single integer ie Source node . 下一行包含单个整数,即Source节点。

Output : 输出:

Print shortest distance to all node from given source node . 打印到给定源节点到所有节点的最短距离。 If node is unreachable print -1. 如果节点不可达,请打印-1。

eg 例如

Input : 输入:

1
6 8
1 2 1
1 5 4
2 5 2
2 3 2
5 6 5
3 6 2
3 4 1
6 4 3
1

Output : (shortest distance of all node from node 1) 输出:(所有节点到节点1的最短距离)

1 3 4 3 5

I appreciate the answers to my question and at the time I chose the Map answer because given my limited understanding of the language, it seemed easier for me to implement. 我很欣赏我的问题的答案,在我选择Map答案的时候,因为对语言的理解有限,所以我似乎更容易实现。

It turns out that I overlooked an important detail that made the problem much simpler than I thought it was: if I maintain an array of distances and insert the nodes into the heap (instead of the distances), to use as references to the distance array, I was able to sort the nodes based on their values. 事实证明,我忽略了一个重要的细节,使问题比我想象的要简单得多:如果我维护一个距离数组并将节点(而不是距离)插入堆中,则用作距离数组的引用,我能够根据其值对节点进行排序。

In this implementation, I didn't need to contrive a key-value property after all. 在此实现中,我根本不需要构造键值属性。 After updating the values in the distance array, I had to remove and re-add those specific nodes to the heap in order for the heap to stay current and sorted, as suggested by @reprogrammer. 更新distance数组中的值后,我必须删除这些特定节点并将其重新添加到堆中,以使堆保持最新并按@reprogrammer的建议进行排序。

Once I changed what I was putting into the heap, the algorithm was very similar to the one found on Wikipedia . 一旦更改了要放入堆中的内容,该算法与Wikipedia上的算法非常相似。

Here is the code I ended up using, in case anyone has the same problem. 如果有人遇到同样的问题,这是我最终使用的代码。 Note: the magic part is the creation of the PriorityQueue (which is similar to what was suggested by @stevevls): 注意:最神奇的部分是PriorityQueue的创建(与@stevevls建议的类似):

import java.util.*;
import java.io.File; //Because files were used to test correctness.
import java.lang.Math;

public class Dijkstra{

//This value represents infinity.
public static final int MAX_VAL = (int) Math.pow(2,30);

/* Assumptions:
    If G[i][j] == 0, there is no edge between vertex i and vertex j
    If G[i][j] > 1, there is an edge between i and j and the value of G[i][j] is its weight.
    No entry of G will be negative.
*/


static int dijkstra(int[][] G, int i, int j){
    //Get the number of vertices in G
    int n = G.length;

    // The 'i' parameter indicates the starting node and the 'j' parameter
    // is the ending node.


    //Create a list of size n of shortest paths, initialize each entry to infinity
    final int[] shortestPaths = new int[n];

    for(int k = 0; k < n; k++){
        shortestPaths[k] = MAX_VAL;
    }

    //Initialize starting node distance to zero.
    shortestPaths[i] = 0;

    //Make a Priority Queue (a heap)
    PriorityQueue<Integer> PQ = new PriorityQueue<Integer>(n,
        new Comparator<Integer>()
            {
                public int compare(Integer p, Integer q)
                {
                    return shortestPaths[p] - shortestPaths[q];
                }
            } );

    //Populate the heap with the nodes of the graph
    for(int k = 0; k < n; k++){
        PQ.offer(k);
    }

    //While the heap has elements.
    while(PQ.size() > 0){

    //  Remove the minimum node distance from the heap.
        int minimum = PQ.poll();

    //  Check if graph is disconnected, if so, return -1.
        if(shortestPaths[minimum] == MAX_VAL)
            {
                return -1;
            }
    //  End node has been reached (i.e. you've found the shortest path), return the distance.
        if( minimum == j){
            return shortestPaths[j];
        }

    //  Take the current node and look through the row to see the vertices adjacent to it (neighbours)
        for(int columnIt = 0; columnIt < n; columnIt ++){


    //    Update the distances in the heap for the current node, using the following calculation:
    //      min((deleted node distance + adjacent edge weight), current node's distance)

            if(G[minimum][columnIt] > 0){

                int sum = shortestPaths[minimum] + G[minimum][columnIt];

                shortestPaths[columnIt]= Math.min(sum, shortestPaths[columnIt]);

                if(shortestPaths[columnIt]==sum)
                {
                    PQ.remove(columnIt);
                    PQ.offer(columnIt);
                }
            }
        }
    }
    return -1;
}

Thank you for your answers and advice. 感谢您的回答和建议。


I am resolving the same issue. 我正在解决相同的问题。 I know where you can find the answer on you question. 我知道您可以在哪里找到问题的答案。 It's a great book with example of code - Algorithms, 4th Edition by Robert Sedgewick and Kevin Wayne. 这是一本很棒的书,上面有代码示例-Robert Sedgewick和Kevin Wayne编写的第四版算法。


Site book and Example of code (including an implementation of Dijkstra's algorithm using a PriorityQueue ) This implementation of Dijkstra's algorithm doesn't use Java's standard PriorityQueue implementation. 网站的书代码示例 (包括使用一个PriorityQueue Dijkstra算法的实现)Dijkstra算法不使用Java标准的PriorityQueue实现此实现。 Instead it implements IndexMinPQ , which was discussed earlier in the book with a detailed explanation! 相反,它实现了IndexMinPQ ,这在本书前面已经进行了详细的解释!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM