I am new to StackOverflow and i just want to say thank you for all of you guys hard work!
I am having a very strange behavior with my program to compute the shortest path from information given in a file.
My file has a format of
Source1 Dest Cost Dest Cost Dest Cost
Source2 Dest Cost Dest Cost Dest Cost
...
I have definitely checked to see if my graph is built correctly and it has been. Dijkstra's algorithm works for any path except for a path starting from the source of the first line to any other line after it. In short, if I run the algorithm for Source1
to say a Dest
that isn't included in its own line, it will print that it cannot be reached. This is completely mysterious to me and I have been trying to find this elusive bug for hours on end for 2 days.
Here is my code:
public class AirportGraph {
/**
* This class represents a vertex in the graph and also maintains its list of adjacent vertices as well as
* the list of all edges connected to the vertex
* @author James
*
*/
private static class AirportVertex implements Comparable<AirportVertex> {
private final int INFINITY = Integer.MAX_VALUE;
private String name;
private int dist;
private boolean known;
private AirportVertex path;
private HashMap<AirportVertex, Integer> adj_lst;
/**
* AirportVertex Constructor
* @param n - name of vertex
*/
public AirportVertex(String n) {
this.known = false;
this.name = n;
this.adj_lst = new HashMap<>();
}
/**
* Method to print the shortest path generated by Dijkstras algorithm to a vertex from a source
*/
private void printPath() {
if (this == this.path) {
System.out.printf("%s", this.name);
} else if (this.path == null) {
System.out.printf("%s(no path)\n", this.name);
} else {
System.out.println("recursing to print");
this.path.printPath();
System.out.printf(" -> %s(%d)", this.name, this.dist);
}
}
/**
* Comparable method, compares to vertices based on their dist value
*/
@Override
public int compareTo(AirportVertex v) {
return Integer.compare(this.dist, v.dist);
}
}
private final int DEFAULT_SIZE = 60;
private final float DEFAULT_LOAD = 0.5f;
private int num_nodes;
private File file;
private HashMap<String, AirportVertex> airports;
/**
* Constructor
* @param file - file to be processed
*/
public AirportGraph(File f) {
this.num_nodes = 0;
this.airports = new HashMap<>(DEFAULT_SIZE, DEFAULT_LOAD);
this.file = f;
processFile(f);
}
/**
* This method will process the file and create the appropriate graph from the formatted file.
* @param file - file to be processed
*/
private void processFile(File f) {
Scanner sc;
/* First we must try to create a scanner from the file */
try {
sc = new Scanner(f);
initGraph(sc);
sc.close();
sc = new Scanner(f);
buildAdjacencyLists(sc);
sc.close();
}
catch (FileNotFoundException e) {
System.err.println("File could not be found");
e.printStackTrace();
}
}
/**
* Method to initialize the graph with empty lists from the file
* Initializes the set of vertices here
* @param sc - Scanner object for airports.txt
*/
private void initGraph(Scanner sc) {
String line;
String from;
while(sc.hasNextLine()) {
line = sc.nextLine();
Scanner tokens = new Scanner(line);
/* Source for each line */
from = tokens.next();
if (!this.airports.containsKey(from))
this.airports.put(from, new AirportVertex(from));
this.num_nodes++;
/* Close the scanner to stop resource leaks */
tokens.close();
}
}
/**
* Method to build the adjacency list of the graph for each vertex
* @param sc - scanner that holds file information
*/
private void buildAdjacencyLists(Scanner sc) {
String line;
String from;
while(sc.hasNextLine()) {
line = sc.nextLine();
Scanner tokens = new Scanner(line);
/* Source for each line */
from = tokens.next();
/* Read the rest of the line */
while (tokens.hasNext()) {
/* The file has a pattern of alternating strings and ints after the first string read on each line */
String dest = tokens.next();
int cost = tokens.nextInt();
/* add the destination to the source hashtable with associated cost */
this.airports.get(from).adj_lst.put(this.airports.get(dest), cost);
}
}
}
/**
* Method that implements Dijkstras algorithm
* @param s - starting vertex to start computing shortest path from
*/
protected void dijkstras(AirportVertex s) {
TreeSet<AirportVertex> q = new TreeSet<>();
/* Make sure that the vertices are initialized correctly and add them to the min heap */
for (AirportVertex v : airports.values()) {
if (v.equals(s)) {
v.path = s;
v.dist = 0;
}
else {
v.path = null;
v.dist = Integer.MAX_VALUE;
}
q.add(v);
System.out.println("Vertex added to q is: " + v.name);
}
System.out.println();
AirportVertex u, v;
/* Loop while the heap isnt empty */
while (!q.isEmpty()) {
/* Vertex with shortest distance */
u = q.pollFirst();
// System.out.println("\nVertex with shortest distance " + u.name);
if (u.dist == Integer.MAX_VALUE)
break;
/*look at distances for each adjacent vertex*/
for (Map.Entry<AirportVertex, Integer> adj : u.adj_lst.entrySet()) {
// System.out.println("Adjacent vertex name: " + adj.getKey().name);
/*This is the adjacent vertex for this iteration */
v = adj.getKey();
int updated_dist = u.dist + adj.getValue();
/* A shorter path has been found, remove v update its dist and path and add it to the end of the heap */
if (updated_dist < v.dist) {
// System.out.println("\nShorter path to neighbor found: " + v.name);
q.remove(v);
v.dist = updated_dist;
v.path = u;
q.add(v);
}
}
}
System.out.println();
}
/**
* Method to print the paths of all vertices
*/
public void printAllPaths() {
for (AirportVertex v : this.airports.values()) {
v.printPath();
System.out.println();
}
}
/**
* Method to run printPath for a given vertex - see printPath method in AirportVertex class
* @param v - source vertex
*/
protected void printPath(String v) {
if (!airports.containsKey(v)) {
System.err.println("Your end name is not valid");
return;
}
airports.get(v).printPath();
System.out.println();
}
/**
* Method to display the graph, only vertices and adjacent vertices are shown
*/
protected void printGraph() {
System.out.printf("%25s\n----------------------------------------------\n", "Adjacency List");
for(AirportVertex v: this.airports.values()) {
System.out.print("Airport: " + v.name + "| Adjacent Airports - ");
for(AirportVertex w : v.adj_lst.keySet())
System.out.print(" " + w.name);
for(int c : v.adj_lst.values())
System.out.print("| Cost: " + c);
System.out.println();
}
}
/**
* Method to return the number of vertices in the graph
* @return numNodes
*/
protected int getNumNodes() {
return this.num_nodes;
}
/**
* Method to return the number of edges in the graph
* @return numEdges
*/
protected AirportVertex getNode(String s) {
return this.airports.get(s);
}
}
Here is the file:
ATL BOS 250 DFW 250 MOB 59
AUS DFW 59 HOU 59 SAT 59
BOS ATL 250 DFW 250
DFW AT50 AUS 59 BOS 250 HOU 128 LAX 1000 LIT 59 MSY 128 OKC 59 SHV 59 SFO 1200
HOU AUS 59 DFW 128 SAT 59
LAX DFW 1000 SFO 100
LIT DFW 59
MOB ATL 59
MSY DFW 128
OKC DFW 59
SAT AUS 59 HOU 59
SFO DFW 1200 LAX 100
SHV DFW 59
Here is my output:
Adjacency List
----------------------------------------------
Airport: LAX| Adjacent Airports - DFW SFO| Cost: 1000| Cost: 100
Airport: SAT| Adjacent Airports - AUS HOU| Cost: 59| Cost: 59
Airport: DFW| Adjacent Airports - BOS AUS LAX MSY LIT HOU SFO OKC ATL SHV| Cost: 250| Cost: 59| Cost: 1000| Cost: 128| Cost: 59| Cost: 128| Cost: 1200| Cost: 59| Cost: 250| Cost: 59
Airport: SFO| Adjacent Airports - DFW LAX| Cost: 1200| Cost: 100
Airport: AUS| Adjacent Airports - DFW SAT HOU| Cost: 59| Cost: 59| Cost: 59
Airport: SHV| Adjacent Airports - DFW| Cost: 59
Airport: MOB| Adjacent Airports - ATL| Cost: 59
Airport: OKC| Adjacent Airports - DFW| Cost: 59
Airport: BOS| Adjacent Airports - DFW ATL| Cost: 250| Cost: 250
Airport: HOU| Adjacent Airports - DFW AUS SAT| Cost: 128| Cost: 59| Cost: 59
Airport: MSY| Adjacent Airports - DFW| Cost: 128
Airport: LIT| Adjacent Airports - DFW| Cost: 59
Airport: ATL| Adjacent Airports - BOS DFW MOB| Cost: 250| Cost: 250| Cost: 59
The number of Vertices in the graph is: 13
Vertex added to q is: LAX
Vertex added to q is: SAT
Vertex added to q is: DFW
Vertex added to q is: SFO
Vertex added to q is: AUS
Vertex added to q is: SHV
Vertex added to q is: MOB
Vertex added to q is: OKC
Vertex added to q is: BOS
Vertex added to q is: HOU
Vertex added to q is: MSY
Vertex added to q is: LIT
Vertex added to q is: ATL
Printing all paths
--------------------------
LAX(no path)
SAT(no path)
recursing to print
ATL -> DFW(250)
SFO(no path)
AUS(no path)
SHV(no path)
recursing to print
ATL -> MOB(59)
OKC(no path)
recursing to print
ATL -> BOS(250)
HOU(no path)
MSY(no path)
LIT(no path)
ATL
Vertex added to q is: LAX
Vertex added to q is: SAT
Vertex added to q is: DFW
Vertex added to q is: SFO
Vertex added to q is: AUS
Vertex added to q is: SHV
Vertex added to q is: MOB
Vertex added to q is: OKC
Vertex added to q is: BOS
Vertex added to q is: HOU
Vertex added to q is: MSY
Vertex added to q is: LIT
Vertex added to q is: ATL
Finding the shortest path to HOU from ATL
-----------------------------------------
HOU(no path)
Vertex added to q is: LAX
Vertex added to q is: SAT
Vertex added to q is: DFW
Vertex added to q is: SFO
Vertex added to q is: AUS
Vertex added to q is: SHV
Vertex added to q is: MOB
Vertex added to q is: OKC
Vertex added to q is: BOS
Vertex added to q is: HOU
Vertex added to q is: MSY
Vertex added to q is: LIT
Vertex added to q is: ATL
Finding the shortest path to SAT from ATL
-----------------------------------------
SAT(no path)
Vertex added to q is: LAX
Vertex added to q is: SAT
Vertex added to q is: DFW
Vertex added to q is: SFO
Vertex added to q is: AUS
Vertex added to q is: SHV
Vertex added to q is: MOB
Vertex added to q is: OKC
Vertex added to q is: BOS
Vertex added to q is: HOU
Vertex added to q is: MSY
Vertex added to q is: LIT
Vertex added to q is: ATL
Finding the shortest path to MOB from ATL
-----------------------------------------
recursing to print
ATL -> MOB(59)
Vertex added to q is: LAX
Vertex added to q is: SAT
Vertex added to q is: DFW
Vertex added to q is: SFO
Vertex added to q is: AUS
Vertex added to q is: SHV
Vertex added to q is: MOB
Vertex added to q is: OKC
Vertex added to q is: BOS
Vertex added to q is: HOU
Vertex added to q is: MSY
Vertex added to q is: LIT
Vertex added to q is: ATL
Finding the shortest path to ATL from SHV
-----------------------------------------
recursing to print
recursing to print
SHV -> DFW(59) -> ATL(309)
Vertex added to q is: LAX
Vertex added to q is: SAT
Vertex added to q is: DFW
Vertex added to q is: SFO
Vertex added to q is: AUS
Vertex added to q is: SHV
Vertex added to q is: MOB
Vertex added to q is: OKC
Vertex added to q is: BOS
Vertex added to q is: HOU
Vertex added to q is: MSY
Vertex added to q is: LIT
Vertex added to q is: ATL
Finding the shortest path to ATL from LAX
-----------------------------------------
recursing to print
recursing to print
LAX -> DFW(1000) -> ATL(1250)
Vertex added to q is: LAX
Vertex added to q is: SAT
Vertex added to q is: DFW
Vertex added to q is: SFO
Vertex added to q is: AUS
Vertex added to q is: SHV
Vertex added to q is: MOB
Vertex added to q is: OKC
Vertex added to q is: BOS
Vertex added to q is: HOU
Vertex added to q is: MSY
Vertex added to q is: LIT
Vertex added to q is: ATL
Finding the shortest path to ATL from DFW
-----------------------------------------
recursing to print
DFW -> ATL(250)
As you can see, ATL is the first Source
that I have described and is giving me this problem. I cannot figure out why this is so. It is being processed the same way as every other line in the file. If you notice, when I call printAllPaths()
, it really shows the problem of what is happening. The algorithm tries to print all paths from the first line but never considers anything after that. But yet if I want to find the shortest path from any other line to anywhere else, including ATL, it works just fine. Everything works but that one line. I have no idea why.
Can anyone help me find this bug?