1 Summary

Returning Routes

Kathi Fisler

Now that we have hasRoute working (and terminating!), let’s extend the problem to return the actual route to the given city. A route here will be a LinkedList of the nodes along the root, in order from the initial node to the named city.

Let’s start with the test cases.

With all that in hand, let’s actually write out the Java checkExpect statements for some tests:

  boolean testhp (Tester t) {

    return t.checkExpect(hart.findRoute("Providence"),

                         new LinkedList<Node>());

  }

  

  boolean testbb (Tester t) {

    LinkedList<Node> expected = new LinkedList<Node>();

    expected.add(bost);

    return t.checkExpect(bost.findRoute("Boston"), expected);

  }

  

  boolean testbw (Tester t) {

    LinkedList<Node> expected = new LinkedList<Node>();

    expected.add(bost);

    expected.add(worc);

    return t.checkExpect(bost.findRoute("Worcester"), expected);

  }

Note that our test functions now need more than just the return statement. In order to build up the expected answer, we need additional expressions to create and populate the result list.

With our test cases in hand, let’s turn to writing the function. We start by simply renaming our previous code from hasRoute to findRoute (similarly for the helper methods) and changing the return type to a list of nodes:

  LinkedList<Node> findRoute(String tocity) {

    return this.findRouteVisit(tocity, new LinkedList<Node>());

  }

  

  private LinkedList<Node> findRouteVisit(String tocity,

                                          LinkedList<Node> visited) {

    if (visited.contains(this))

      return false;

    else if (this.cityname.equals(tocity))

      return true;

    else {

      visited.add(this);

      return this.findRouteConnects(tocity, visited);

    }

  }

  

  private LinkedList<Node>

   findRouteConnects(String tocity, LinkedList<Node> visited) {

    for (Node c : this.connects) {

      if (c.findRouteVisit(tocity, visited))

        return true;

    }

    return false;

  }

Clearly, this code won’t compile because it is returning booleans (the old return type) instead of lists. At the minimum, we need to replace all the boolean return values with appropriate lists.

The false cases are easiest: we used to return false when there was no route. We denote that now by returning an empty list. So we will edit each return false to return an empty list:

  LinkedList<Node> findRoute(String tocity) {

    return this.findRouteVisit(tocity, new LinkedList<Node>());

  }

  

  private LinkedList<Node> findRouteVisit(String tocity,

                                          LinkedList<Node> visited) {

    if (visited.contains(this))

      return new LinkedList<Node>();

    else if (this.cityname.equals(tocity))

      return true;

    else {

      visited.add(this);

      return this.findRouteConnects(tocity, visited);

    }

  }

  

  private LinkedList<Node>

   findRouteConnects(String tocity, LinkedList<Node> visited) {

    for (Node c : this.connects) {

      if (c.findRouteVisit(tocity, visited))

        return true;

    }

    return new LinkedList<Node>();

  }

This leaves updating the former true return values. Previously, we returned true when we had found a route that involved the current this node. The only difference now is that we have to return a route that includes this. When the cityname equals the target city, the whole route consists of the this node. The following version of findRouteVisit shows the code:

  private LinkedList<Node> findRouteVisit(String tocity,

                                          LinkedList<Node> visited) {

    if (visited.contains(this))

      return new LinkedList<Node>();

    else if (this.cityname.equals(tocity)) {

      LinkedList<Node> prevRoute = new LinkedList<Node>();

      prevRoute.addFirst(this);

      return prevRoute;

    }

    else {

      visited.add(this);

      return this.findRouteConnects(tocity, visited);

    }

  }

In findRouteConnects, we were returning true based on a route search from the nodes in this.connects. In findRoute, we must get the route returned from searching each node, and if it isn’t empty (which indicates that a route exists), add this to the route. The code is as follows:

  private LinkedList<Node>

   findRouteConnects(String tocity, LinkedList<Node> visited) {

    for (Node c : this.connects) {

      LinkedList<Node> connectRoute = c.findRouteVisit(tocity,

                                                       visited);

      if (connectRoute.size() > 0) {

        connectRoute.addFirst(this);

        return connectRoute;

      }

    }

    return new LinkedList<Node>();

  }

Both pieces of code illustrate a new concept in Java: defining local variables (prevRoute and connectRoute). The first line mentioning each of these variables gives it a type and an initial value. The variable is visible only within its local area of code (the else if body for prevRoute and the for body for connectRoute).

How do we argue that this program terminates? The argument is identical to that for hasRoute, since we didn’t change the traversal code, only the return value on each node.

1 Summary

From the graphs perspective, this problem mostly just gave another example of a program that traverses a graph. However, we used it to illustrate several other important points: