简体   繁体   中英

Dijkstra algorithm in weighted graph in Java with big graph

My task is to write a program to calculate shortest time it takes to go from node A(start) to node B(end) on a weighted graph(I'm using dijkstra algorithm). Time requirement is 2 seconds on memory limit is 64 MB and I can assume that entered data is valid
First line has 4 whole numbers:
Amount of nodes M(2 ≤ M ≤ 20000)
Amount of edges(connections) N(0 ≤ N ≤ 50000)
Start node A(0 ≤ A < M) and end node O (0 ≤ O < M).

Next N lines has connection info represented with three whole numbers:
connected computers and the time between them in milliseconds(edge weight). My code doesn't pass all the tests. If i test my code with a big graph then it fails as heap memory runs out(about 900MB on my machine).
My problem : How do i optimize my code so it fits into requirements?
Would using adjacency list be enough?
My code is this:

import java.util.ArrayList;
import java.util.Scanner;

public class Route3 {
  public static int minDistance(ArrayList<Long> mindist, ArrayList<Boolean> visited){
    long min = Long.MAX_VALUE;
    int minindex = -1;
    for (int i = 0; i < mindist.size(); i++) {
        if(!visited.get(i) && (mindist.get(i) <= min)) {
            min = mindist.get(i);
            minindex = i;
        }
    }
    return minindex;
}
public static long dijkstra(long[][] graph, int start, int end) {
    int computers = graph.length;
    ArrayList<Boolean> traversed = new ArrayList<>(); //Hold traversed nodes
    for (int i = 0; i < computers; i++) {
        traversed.add(i,false);
    }
    ArrayList<Long> mindist = new ArrayList<>(); //Holds mindistances to nodes based on index
    for (int i = 0; i < computers; i++) {
        mindist.add(i,Long.MAX_VALUE);
    }
    mindist.set(start,(long)0);

    for (int i = 0; i < computers; i++) {
        int min = minDistance(mindist,traversed);
        if(min == -1) return mindist.get(end);
        traversed.set(min,true);
        if(min == end) return mindist.get(min) == Long.MAX_VALUE ? -1 : mindist.get(min); //Error check
        for (int j = 0; j < computers; j++) {
            if(!traversed.get(j) && graph[min][j] != 0 && mindist.get(min) + graph[min][j] < mindist.get(j)) {
                mindist.set(j,(mindist.get(min) + graph[min][j]));
            }

        }

    }
    return mindist.get(end) == Long.MAX_VALUE ? -1 : mindist.get(end);
}
public static void main(String[] args){
    Scanner in = new Scanner(System.in);
    int computers = in.nextInt(); //nodes
    int connections = in.nextInt(); //edges
    int start = in.nextInt();
    int end = in.nextInt();

    long[][] graph = new long[computers+1][computers+1];

    for (int i = 0; i < connections; i++) {
        int x = in.nextInt();
        int y = in.nextInt();
        long t = in.nextLong();
        graph[x][y] = t;
        graph[y][x] = t;
    }
    if(connections == 0) {
        System.out.println(-1);
        System.exit(0);
    }
    long dist = dijkstra(graph,start,end);
    if(dist != -1) System.out.println(dist);
    else System.out.println(-1);
 }
}

All help is appreciated!

I think that you'll have to find a better way to hold the graph information. This line:

long[][] graph = new long[computers+1][computers+1]

could take up to 20001*20001*8 bytes, that is 3GB!

As your net has few connections per node I'd suggest storing the graph as a HashMap of HashMaps of connections:

class Connection {
    int nodeA, nodeB;
    long time;
}

HashMap<Integer, HashMap<Integer, Connection>> graph;

It may sound less efficient, but you are saving all the blank edges. Then you add the Connection to the graph indexed by both nodes (nodeA and nodeB):

void addConnection(Connection c)
{
   HashMap<Integer, Connection> subgraph = graph.get(c.nodeA);
   if(subgraph == null)
       subgraph = new HashMap<>();
   subgraph.put(c.nodeB, c);

   HashMap<Integer, Connection> subgraph = graph.get(c.nodeB);
   if(subgraph == null)
       subgraph = new HashMap<>();
   subgraph.put(c.nodeA, c);
}

And retrieve the connection descriptor like:

long getConnection(int nodeA, int nodeB)
{
    ArrayList<Connection> subgraph = graph.get(nodeA);
    if(subgraph == null)
        return 0L;
    Connection c = subgraph.get(nodeB);
    if(c == null)
        return 0L;
    return c.time;
}

This should make your program slower but much more memory efficient.

WARNING: The code is not tested, it's just for clarification.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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