More breadth-first search
Reachability with BFS
Last time we implemented a function to get the number of flights between two airports. We implemented it using breadth-first search, which starts at a given node and then visits all of the nodes it can get to by following edges, in order of how far away they are from the starting node. Here is our distance method:
def distance(graph: FlightGraph, start: String, end: String): Int = { var distances = Map(start -> 0) var visit = List(start) while (!visit.isEmpty) { val node = visit.head visit = visit.tail for (neighbor <- graph.neighbors(node)) { if (!distances.contains(neighbor)) { distances = distances + (neighbor -> (distances(node) + 1)) if (neighbor == end) { return distances(neighbor) } visit = visit :+ neighbor } } } -1 }
isReachable
We can answer other questions using breadth-first search (BFS). For instance: is
LAX reachable from BTV (i.e., can we get there at all)? In order to implement an
isReachable
function to answer this question, we can modify our distance
function so that it doesn’t track distances. We could start with something like this:
def isReachable(graph: FlightGraph, start: String, end: String): Boolean = { var visit = List(start) while (visit.nonEmpty) { val node = visit.head visit = visit.tail for (neighbor <- graph.neighbors(node)) { if (neighbor == end) { return true } visit = visit :+ neighbor } } return false }
This function has an issue, though: when end
is not reachable from start
,
it will loop forever! This happens because we’re not keeping track of the nodes
we visit, so we will keep visiting the same nodes over and over. For the
distance
method, we didn’t have this problem because we checked to see if we
had already calculated the distance to a node before visiting it. In this case,
we can track the set of visited
nodes.
def isReachable(graph: FlightGraph, start: String, end: String): Boolean = { var visited = Set(start) var visit = List(start) while (visit.nonEmpty) { val node = visit.head visit = visit.tail for (neighbor <- graph.neighbors(node)) { if (!visited.contains(neighbor)) { visited = visited + neighbor if (neighbor == end) { return true } visit = visit :+ neighbor } } } return false }
allReachable
How about another reachability quesiton: how can we find all of the nodes that
are reachable from a particular starting node? To do this, we can just return
the visited
set we were tracking in isReachable
:
def allReachable(graph: FlightGraph, start: String): Set[String] = { var visited = Set(start) var visit = List(start) while (visit.nonEmpty) { val node = visit.head visit = visit.tail for (neighbor <- graph.neighbors(node)) { if (!visited.contains(neighbor)) { visited = visited + neighbor visit = visit :+ neighbor } } } return visited }
Paths
We ended by brainstorming a method to get the best path between two nodes. See the lecture capture for details.