Information

Author(s) François Aubry
Deadline No deadline
Submission limit No limitation

Sign in

Graphs - Finding a path

In this task we will focus on the problem of finding a path between two given nodes in a graph.

Let \(s\) be the source node and \(t\) the destination node. We will keep which nodes have already been visited and which nodes still need to be processed in a queue. At each iteration we pick the node in front of the queue to be processed. Then we add all of its unvisited neighbours to the queue of nodes to process. If we continue in this way until there are no more nodes to process then we know that there is path from \(s\) to \(t\) if and only if \(t\) was visited.

Example:

The following figure shows an execution of this process with \(s = 0\).

graphs-pathfind/anim.gif

Let's see what this looks like in Java. We will create a function that takes as input the graph \(g\), the source \(s\) and the destination \(t\). We start by adding the source to the queue and then process the nodes until no more nodes remain to be processed. To process a node, we loop over its neighbours and add all its unvisited neighbours to the queue so they are processed later. In the end, all nodes that are reachable from \(s\) will have been visited.

static boolean pathExists(LinkedList<Integer>[] g, int s, int t) {
    // initialize the queue and visited set
    Queue<Integer> Q = new LinkedList<>();
    Q.add(s);
    BitSet visited = new BitSet();
    visited.set(s);
    // loop while there are nodes in the queue to process
    // in practice you might want to stop as soon as end is visited
    while(!Q.isEmpty()) {
        int u = Q.poll();
        // we are now processing node u
        for(int v : g[u]) {
            // visit edge (u, v)
            if(!visited.get(v)) {
                // node v has not yet been visited, add  it
                Q.add(v);
                visited.set(v);
            }
        }
    }
    // return whether a path exists
    return visited.get(t);
}

Ok so now we have a function that checks whether a path exists. Now we are going to extend it so that we are able to compute an actual path between \(s\) and \(t\).

Doing this is quite simple. We just need to keep track, for each node, of who added it to the queue. If we have this then we can start from \(t\) and find who visited him, say \(u\). Then we check who put \(u\) in the queue and continue in this way until we reach the source \(s\). Note that we are guaranteed to reach \(s\) since this was the node on which we started the search.

Observe that by doing this we will build the path in reverse order because we start from \(t\) and trace back to \(s\). Therefore we need to reverse the list of nodes in the end.

static ArrayList<Integer> findPath(LinkedList<Integer>[] g, int s, int t) {
    // initialize the queue and visited set
    Queue<Integer> Q = new LinkedList<>();
    Q.add(s);
    BitSet visited = new BitSet();
    visited.set(s);
    // initialize the parent array
    Integer[] parent = new Integer[g.length];
    // loop while there are nodes in the queue to process
    // in practice you might want to stop as soon as end is visited
    while(!Q.isEmpty()) {
        int u = Q.poll();
        // we are now processing node u
        for(int v : g[u]) {
            // visit edge (u, v)
            if(!visited.get(v)) {
                // node v has not yet been visited, add it
                Q.add(v);
                visited.set(v);
                // set the parent of v to be u
                parent[v] = u;
            }
        }
    }
    // check whether a path exists
    if(!visited.get(t)) return null;
    // build the path by tracing back from t to s
    ArrayList<Integer> path = new ArrayList<>();
    Integer cur = t;
    // loop until we reach s (s is the only visited node with null parent)
    while(cur != null) {
        path.add(cur);
        cur = parent[cur];
    }
    // return the path
    Collections.reverse(path);
    return path;
}

Find the path

Given a graph \(g\) and two nodes \(s\) and t, find a path from \(s\) to \(t\).

Input

  • One line with two integers \(n\) and \(m\) giving the number of nodes in the graph and the number of edges respectively.
  • \(m\) lines each with two integers \(u\) and \(v\) giving that there is an edge from \(u\) to \(v\). No edge will appear more than once in the input.
  • One line with two integers \(s\) and \(t\) giving the source and the destination.

Output

If there is a path from \(s\) to \(t\) print a single line with the nodes of that path separated by single spaces.

If no path exists print impossible.

Constraints

  • \(1 \leq n \leq 1000\)
  • \(1 \leq m \leq n (n - 1) / 2\)
  • \(0 \leq u, v, s, t < n\)

Sample Test Cases

Sample input 1

Sample output 1

Sample input 2

Sample output 2

Sample input 3

Sample output 3


Max file size: 1.0 MiB
Allowed extensions: .java, .cpp, .py