
directed path from 8 to 1: red lines
directed cycle: blue lines
vertex 6 (green) has outdegree=4 and indegree=2






| digraph | vertex | directed edge |
|---|---|---|
| transportation | street intersection | one-way street |
| web | web page | hyperlink |
| food web | species | predator-prey relationship |
| WordNet | synset | hypernym |
| scheduling | task | precedence constraint |
| financial | bank | transaction |
| cell phone | person | placed call |
| infectious disease | person | infection |
| game | board position | legal move |
| citation | journal article | citation |
| object graph | object | pointer |
| inheritance hierarchy | class | inherits from |
| control flow | code block | jump |
| problem | description |
|---|---|
| s→t path | Is there a path from \(s\) to \(t\)? |
| shortest s→t path | What is the shortest path from \(s\) to \(t\)? |
| directed cycle | Is there a directed cycle in the graph? |
| topological sort | Can the digraph be drawn so that all edges point upwards? |
| strong connectivity | Is there a directed path between all pairs of vertices? |
| transitive closure | For which vertices \(v\) and \(w\) is there a directed path from \(v\) to \(w\)? |
| PageRank | What is the importance of a web page? |
Almost identical to Graph API

Maintain vertex-indexed array of lists
![]() |
![]() |
In practice: use adjacency-lists representations
| representation | space | insert edge from \(v\) to \(w\) | edge from \(v\) to \(w\)? | iterate over vertices pointing from \(v\) |
|---|---|---|---|---|
| list of edges | \(E\) | \(1\) | \(E\) | \(E\) |
| adjacency matrix | \(V^2\) | \(1^\dagger\) | \(1\) | \(V\) |
| adjacency lists | \(E + V\) | \(1\) | \(\mathrm{outdegree}(v)\) | \(\mathrm{outdegree}(v)\) |
\(^\dagger\)disallows parallel edges
public class Graph {
private final int V;
private final Bag<Integer>[] adj; // adj lists
public Graph(int V) {
// create empty graph with V vertices
this.V = V;
adj = (Bag<Integer>[]) new Bag[V];
for(int v = 0; v < V; v++)
adj[v] = new Bag<Integer>();
}
public void addEdge(int v, int w) {
// add edge v-w (parallel edges and self-loops allowed)
adj[v].add(w);
adj[w].add(v);
}
// iterator for vertices adjacent to v
public Iterable<Integer> adj(int v) {
return adj[v];
}
}
public class Digraph {
private final int V;
private final Bag<Integer>[] adj; // adj lists
public Digraph(int V) {
// create empty graph with V vertices
this.V = V;
adj = (Bag<Integer>[]) new Bag[V];
for(int v = 0; v < V; v++)
adj[v] = new Bag<Integer>();
}
public void addEdge(int v, int w) {
// add edge v->w
adj[v].add(w);
}
// iterator for vertices adjacent to v
public Iterable<Integer> adj(int v) {
return adj[v];
}
}
Problem: Find all vertices reachable from \(s\) along a directed path

Same method as for undirected graphs
DFS (to visit a vertex v)
Mark v as visited
Recursively visit all unmarked verts w pointing from v
To visit a vertex \(v\):
Recall code for undirected graphs.
public class DepthFirstPaths {
private int s;
private boolean[] marked; // true if v connected to s
private int[] edgeTo; // prev vertex on path from s to v
public DepthFirstPaths(Graph G, int s) {
// initialize data structures
/* ... */
// find vertices connected to s
dfs(G, s);
}
// recursive DFS does the work
private void dfs(Graph G, int v) {
marked[v] = true;
for(int w : G.adj(v)) {
if(!marked[w]) {
edgeTo[w] = v;
dfs(G, w);
}
}
}
}
Directed graphs identical to undirected (sub Digraph for Graph)
public class DirectedDFP {
private int s;
private boolean[] marked; // true if v connected to s
private int[] edgeTo; // prev vertex on path from s to v
public DirectedDFP(Diraph G, int s) {
// initialize data structures
/* ... */
// find vertices connected to s
dfs(G, s);
}
// recursive DFS does the work
private void dfs(Diraph G, int v) {
marked[v] = true;
for(int w : G.adj(v)) {
if(!marked[w]) {
edgeTo[w] = v;
dfs(G, w);
}
}
}
}

Every program is a digraph
Dead-code elimination
Infinite-loop detection

Every data structure is a digraph
Roots
Reachable objects
Mark-sweep algorithm [McCarthy, 1960]
Memory Cost: Uses 1 extra bit per object (plus DFS stack.)

DFS enables direct solution of simple digraph problems
Basis for solving difficult digraph problems
Same method as for undirected graphs
BFS (from source vertex s):
Put s onto a FIFO queue, and mark s as visited
Repeat until the queue is empty:
remove the least recently added vertex v
for each unmarked vertex pointing from v:
add to queue and mark as visited
Proposition: BFS computes shortest paths (fewest number of edges) from \(s\) to all other vertices in a digraph in time proportional to \(E+V\)
|
Ex: \(S = \{ 1, 7, 10 \}\) Shortest paths to...
|
![]() |
Q. How to implement multi-source shortest paths algorithm?
Goal: Crawl web, starting from some root web page, say taylor.edu
Solution: (BFS with implicit digraph)
Q. Why not use DFS?
Goal: Given a set of tasks to be completed with precedence constraints, in which order should we schedule the tasks?
Digraph model: vertex = task; edge = precedence constraint
Tasks: COS120, COS121, COS143, COS243, COS265, COS284, COS320, MAT151, MAT215
![]() |
![]() |
Tasks: COS120, COS121, COS143, COS243, COS265, COS284, COS320, MAT151, MAT215
![]() |
![]() |
Topological sort:


Topological sort:

| Postorder | 4 1 2 5 0 6 3 |
| Topological Order | 3 6 0 5 2 1 4 |
public class DepthFirstOrder {
private boolean[] marked;
private Stack<Integer> reversePostorder;
public DepthFirstOrder(Digraph G) {
reversePostorder = new Stack<Integer>();
marked = new boolean[G.V()];
for(int v = 0; v < G.V(); v++)
if(!marked[v]) dfs(G, v);
}
private void dfs(Digraph G, int v) {
marked[v] = true;
for(int w : G.adv(v))
if(!marked[w]) dfs(G, w);
reversePostorder.push(v);
}
// returns all vertices in "reverse DFS postorder"
public Iterable<Integer> reversePostorder() {
return reversePostorder;
}
}
Proposition: Reverse DFS postorder of a DAG is a topological order.
Pf: Consider any edge \(v \rightarrow w\). When dfs(v) is called:
dfs(w) has already been called and returned. Thus, \(w\) was done before \(v\).dfs(w) has not yet been called. dfs(w) will get called directly or indirectly by dfs(v) and will finish before dfs(v). Thus, \(w\) will be done before \(v\).dfs(w) has already been called, but has not yet returned. Can't happen in a DAG: function call stack contains path from \(w\) to \(v\), so \(v \rightarrow w\) would complete a cycle.Proposition: Reverse DFS postorder of a DAG is a topological order.
Proposition: A digraph has a topological order iff no directed cycle.
Pf:
|
Goal: Given a digraph, find a directed cycle. Solution: DFS. What else? see textbook |
![]() |

Remark: A directed cycle implies scheduling problem is infeasible.
The Java compiler does cycle detection.
public class A extends B { }
public class B extends C { }
public class C extends A { }
|
![]() |
$ javac A.java
A.java:1: cyclic inheritence involving A
public class A extends B { }
^
1 error
Microsoft Excel does cycle detection (and has a circular reference toolbar!)


Observation: DFS visits each vertex exactly once. The order in which it does so can be important.
Orderings:
dfs() is calleddfs() returnsdfs() returnsprivate void dfs(Digraph G, int v) {
marked[v] = true;
preorder.enqueue(v); // preorder (queue)
for(int w : G.adv(v))
if(!marked[w]) dfs(G, w);
postorder.enqueue(v); // postorder (queue)
reversePostorder.push(v); // reverse postorder (stack)
}
Def: Vertices \(v\) and \(w\) are strongly connected if there is both a directed path from \(v\) to \(w\) and a directed path from \(w\) to \(v\).
Key property: Strong connectivity is an equivalence relation:
Def: A strong component is a maximal subset of strongly-connected vertices


\(v\) and \(w\) are connected if there is a path between \(v\) and \(w\). use connected component id, which is easy to compute with DFS.
// 0 1 2 3 4 5 6 7 8 9 10 11 12
// id[] = 0 0 0 0 0 0 1 1 1 2 2 2 2
public boolean connected(int v, int w) {
return id[v] == id[w]; // const-time
}

\(v\) and \(w\) are strongly connected if there is both a directed path from \(v\) to \(w\) and a directed path from \(w\) to \(v\). strongly-connected component id (how to compute?)
// 0 1 2 3 4 5 6 7 8 9 10 11 12
// id[] = 1 0 1 1 1 1 3 4 3 2 2 2 2
public boolean stronglyConnected(int v, int w) {
return id[v] == id[w]; // const-time
}
Food web graph

Strong component: subset of species with common energy flow

Software module dependency graph:

Strong component: Subset of mutually interacting modules
Approach 1: Package strong components together
Approach 2: Use to improve design!
1960s: Core OR problem
1972: linear-time DFS algorithm (Tarjan)
1980s: easy two-pass linear-time algorithm (Kosaraju-Sharir)
1990s: more easy linear-time algorithms
Reverse graph: Strong components in \(G\) are same as in \(G^R\)
Kernel DAG: Contract each strong component into a single vertex
Idea:


Phase 1: Compute reverse postorder in \(G^R\)
Phase 2: Run DFS in \(G\), visiting unmarked vertices in reverse postorder of \(G^R\)


Phase 1: Compute reverse postorder in \(G^R\)


0->6->8, 6->7, 0->2->3->4->5, 4->11->9->12->10, 18 7 6 5 10 12 9 11 4 3 2 0 11 0 2 3 4 11 9 12 10 5 6 7 8Phase 2: Run DFS in \(G\), visiting unmarked vertices in reverse postorder of \(G^R\)
1 0 2 3 4 11 9 12 10 5 6 7 81 0 - - - 11 - -- -- - 6 7 -
![]() |
|
Proposition: Kosaraju-Sharir algorithm computes the strong components of a digraph in time proportional to \(E+V\)
Pf:
public class CC {
private boolean marked[];
private int[] id;
private int count;
public CC(Graph G) {
marked = new boolean[G.V()];
id = new int[G.V()];
for(int v = 0; v < G.V(); v++) {
if(!marked[v]) {
dfs(G, v);
count++;
}
}
}
private void dfs(Graph G, int v) {
marked[v] = true;
id[v] = count;
for(int w : G.adj(v)) {
if(!marked[w]) dfs(G, w);
}
}
public boolean connected(int v, int w) {
return id[v] == id[w];
}
}
public class KosarajuSharirSCC {
private boolean marked[];
private int[] id;
private int count;
public KosarajuSharirSCC(Digraph G) {
marked = new boolean[G.V()];
id = new int[G.V()];
DepthFirstOrder dfs = new DepthFirstOrder(G.reverse());
for(int v : dfs.reversePostorder()) {
if(!marked[v]) {
dfs(G, v);
count++;
}
}
}
private void dfs(Digraph G, int v) {
marked[v] = true;
id[v] = count;
for(int w : G.adj(v)) {
if(!marked[w]) dfs(G, w);
}
}
public boolean stronglyConnected(int v, int w) {
return id[v] == id[w];
}
}
| single-source reachability in a digraph |
![]() |
DFS |
| topological sort in a DAG |
![]() |
DFS |
| strong components in a digraph |
![]() |
Kosaraju-Sharir DFS (twice) |