1

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?

James Combs
  • 139
  • 11

1 Answers1

4

OH MY GOSH! After searching for this long and asking this question, I immediately found it. The problem was with my TreeSet data structure. It is only able to have a single value for each key. Therefore, all the elements inside of it that are equal (Have a dist of Integer.MAX_VALUE) are actually never added, or effectively removed.

I fixed this by changing my compareTo() method to the following:

         /**
         * Comparable method, compares to vertices based on their dist value
         */
        @Override
         public int compareTo(AirportVertex v) {
            if (this.dist == v.dist)
                return this.name.compareTo(v.name);
            return Integer.compare(this.dist, v.dist);
        }

This makes sure that any values that are equal are handled correctly by the TreeSet structure.

Im not completely sure why this was only effecting one line of the file. I have a feeling it is because ATL can go to DFW and DFW can go to many, many other places, hence causing lots of cases of duplicate values and essentially destroying everything. But this is just a guess at best.

James Combs
  • 139
  • 11