1 Test Cases
2 The Find Route Method
3 Cleaning Up Find Route
4 Critiquing the Find Route Code

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.

1 Test Cases

Let’s start with the test cases.

2 The FindRoute Method

Having discussed the test cases, let’s turn to writing the method. We will initially write this without worrying about termination, just to illustrate the heart of the computation. The method returns a list containing just the this node if this and to are equal, otherwise it searches from the getsTo node. If one of the getsTo nodes yields a route, the method adds this to the front of the route and returns it. If none of the getsTo nodes yields a route, the method returns an empty list (denoting the lack of a route).

  class Node {

    private String cityname;          // name of city at this node

    private LinkedList<Node> getsTo;  // edges from this Node

  

    // produces a route from this node to the to Node, if one exists.

    // The route should not include the same city more than once

    LinkedList<Node> findRoute(Node to) {

      // if this is the node we are searching for,

      //   return a route with one node

      if (this.equals(to)) {

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

        newRoute.addFirst(this);

        return newRoute;

      }

      // search for route through the getsTo nodes

      else {

        for (Node c : this.getsTo) {

          LinkedList<Node> cRoute = c.findRoute(to);

          if (cRoute.size() > 0) {

            cRoute.addFirst(this);

            return cRoute;

          }

        }

        // no route from children, so return empty list

        return new LinkedList<Node>();

      }

    }

  }

Note that this code is remarkably similar to that for hasRoute: the main difference lies in building and returning a LinkedList of Node rather than a boolean.

In order to make this code terminate, we use a visited parameter, just as we did for hasRoute. The code involving visited is the same in both methods:

  class Node {

    private String cityname;          // name of city at this node

    private LinkedList<Node> getsTo;  // edges from this Node

  

    // produces a route from this node to the to Node, if one exists.

    // The route should not include the same city more than once

    LinkedList<Node> findRoute(Node to, LinkedList<Node> visited) {

      // if have tried this node before, there is no route

      if (visited.contains(this)) {

        return new LinkedList<Node>();

      }

      // if this is the node we are searching for,

      //   return a route with one node

      else if (this.equals(to)) {

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

        newRoute.addFirst(this);

        return newRoute;

      }

      // search for route through the getsTo nodes

      else {

        visited.add(this);

        for (Node c : this.getsTo) {

          LinkedList<Node> cRoute = c.findRoute(to,visited);

          if (cRoute.size() > 0) {  //effectively a typecheck

            cRoute.addFirst(this);

            return cRoute;

          }

        }

        // no route from children, so return empty list

        return new LinkedList<Node>();

      }

    }

  }

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.

3 Cleaning Up FindRoute

The current code works fine, but the method is starting to get a bit long and hard to read. Remember that code needs to be readable by whoever has to maintain it several months from now (could be you, could be another programmer).

To improve the readability a bit, we will move the bulk of the else case that searches for routes via edges into another method, as follows:

  class Node {

    private String cityname;          // name of city at this node

    private LinkedList<Node> getsTo;  // edges from this Node

  

    // produces a route from this node to the to Node, if one exists

    // The route should not include the same city more than once

    LinkedList<Node> findRoute(Node to, LinkedList<Node> visited) {

      // if have tried this node before, there is no route

      if (visited.contains(this)) {

        return new LinkedList<Node>();

      }

      // if this is the node we are searching for,

      //   return a route with one node

      else if (this.equals(to)) {

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

        newRoute.addFirst(this);

        return newRoute;

      }

      // search for route through the getsTo nodes

      else {

        visited.add(this);

        return this.findRouteEdges(to,this,visited);

      }

    }

  

    // checks for a route using the edges out of getsTo

    private LinkedList<Node>

      findRouteEdges(Node to, LinkedList<Node> visited) {

        for (Node c : this.getsTo) {

          LinkedList<Node> cRoute = c.findRoute(to,visited);

          if (cRoute.size() > 0) {

            cRoute.addFirst(this);

            return cRoute;

          }

        }

        // no route from children, so return empty list

        return new LinkedList<Node>();

    }

  }

Note that findRouteEdges has the same contents as the else statement in the previous statement. In creating this separate method, however, we have done two things:

Note also that we made findRouteEdges private, as it is not meant to be called from outside the Node class. Put differently, it is purely a helper method to findRoute.

4 Critiquing the FindRoute Code

Now that we have a working version of findRoute, let’s step back and critique it. What could be improved about this code?

Next up, we’ll work on addressing these issues.