CS 332: Algorithms
Graph Algorithms
Administrative
Test postponed to Friday
Homework:
Turned in last night by midnight: full credit
Turned in tonight by midnight: 1 day late, 10% off
Turned in tomorrow night: 2 days late, 30% off
Extra credit lateness measured separately
Review: Graphs
A graph G = (V, E)
V = set of vertices, E = set of edges
Dense graph: |E| |V|2; Sparse graph: |E| |V|
Undirected graph:
Edge (u,v) = edge (v,u)
No self-loops
Directed graph:
Edge (u,v) goes from vertex u to vertex v, notated uv
A weighted graph associates weights with either the edges
or the vertices
Review: Representing Graphs
Assume V = {1, 2, , n}
An adjacency matrix represents the graph as a n x n
matrix A:
A[i, j] = 1 if edge (i, j) E (or weight of edge)
= 0 if edge (i, j) E
Storage requirements: O(V2)
A dense representation
But, can be very efficient for small graphs
Especially if store just one bit/edge
Undirected graph: only need one diagonal of matrix
Review: Graph Searching
Given: a graph G = (V, E), directed or undirected
Goal: methodically explore every vertex and
every edge
Ultimately: build a tree on the graph
Pick a vertex as the root
Choose certain edges to produce a tree
Note: might also build a forest if graph is not
connected
Review: Breadth-First Search
Explore a graph, turning it into a tree
One vertex at a time
Expand frontier of explored vertices across the
breadth of the frontier
Builds a tree over the graph
Pick a source vertex to be the root
Find (discover) its children, then their children,
etc.
Review: Breadth-First Search
Again will associate vertex colors to guide the
algorithm
White vertices have not been discovered
All vertices start out white
Grey vertices are discovered but not fully explored
They may be adjacent to white vertices
Black vertices are discovered and fully explored
They are adjacent only to black and gray vertices
Explore vertices by scanning adjacency list of grey
vertices
Review: Breadth-First Search
BFS(G, s) {
initialize vertices;
Q = {s}; // Q is a queue (duh); initialize to s
while (Q not empty) {
u = RemoveTop(Q);
for each v u->adj {
if (v->color == WHITE)
v->color = GREY;
v->d = u->d + 1;
v->p = u;
What does v->d
Enqueue(Q, v);
What does v->p
}
u->color = BLACK;
}
}
represent?
represent?
Breadth-First Search: Example
r
Breadth-First Search: Example
r
Q: s
Breadth-First Search: Example
r
Q: w
Breadth-First Search: Example
r
Q: r
Breadth-First Search: Example
r
Q:
Breadth-First Search: Example
r
Q: x
Breadth-First Search: Example
r
Q: v
Breadth-First Search: Example
r
Q: u
Breadth-First Search: Example
r
Q: y
Breadth-First Search: Example
r
Q:
BFS: The Code Again
BFS(G, s) {
initialize vertices;
Touch every vertex: O(V)
Q = {s};
while (Q not empty) {
u = RemoveTop(Q);
u = every vertex, but only once
for each v u->adj {
if (v->color == WHITE)
(Why?)
v->color = GREY;
So v = every vertexv->d = u->d + 1;
that appears in v->p = u;
Enqueue(Q, v);
some other} verts
adjacencyu->color
list
= BLACK;
}
What will be the running time?
}
Total running time: O(V+E)
BFS: The Code Again
BFS(G, s) {
initialize vertices;
Q = {s};
while (Q not empty) {
u = RemoveTop(Q);
for each v u->adj {
if (v->color == WHITE)
v->color = GREY;
v->d = u->d + 1;
v->p = u;
Enqueue(Q, v);
}
u->color = BLACK;
}
}
What will be the storage cost
in addition to storing the graph?
Total space used:
O(max(degree(v))) = O(E)
Breadth-First Search: Properties
BFS calculates the shortest-path distance to the
source node
Shortest-path distance (s,v) = minimum number of
edges from s to v, or if v not reachable from s
Proof given in the book (p. 472-5)
BFS builds breadth-first tree, in which paths to
root represent shortest paths in G
Thus can use BFS to calculate shortest path from one
vertex to another in O(V+E) time
Depth-First Search
Depth-first search is another strategy for
exploring a graph
Explore deeper in the graph whenever possible
Edges are explored out of the most recently
discovered vertex v that still has unexplored edges
When all of vs edges have been explored,
backtrack to the vertex from which v was
discovered
Depth-First Search
Vertices initially colored white
Then colored gray when discovered
Then black when finished
Depth-First Search: The Code
DFS(G)
{
for each vertex u G->V
{
u->color = WHITE;
}
time = 0;
for each vertex u G->V
{
if (u->color == WHITE)
DFS_Visit(u);
}
}
DFS_Visit(u)
{
u->color = GREY;
time = time+1;
u->d = time;
for each v u->Adj[]
{
if (v->color == WHITE)
DFS_Visit(v);
}
u->color = BLACK;
time = time+1;
u->f = time;
}
Depth-First Search: The Code
DFS(G)
{
for each vertex u G->V
{
u->color = WHITE;
}
time = 0;
for each vertex u G->V
{
if (u->color == WHITE)
DFS_Visit(u);
}
}
DFS_Visit(u)
{
u->color = GREY;
time = time+1;
u->d = time;
for each v u->Adj[]
{
if (v->color == WHITE)
DFS_Visit(v);
}
u->color = BLACK;
time = time+1;
u->f = time;
}
What does u->d represent?
Depth-First Search: The Code
DFS(G)
{
for each vertex u G->V
{
u->color = WHITE;
}
time = 0;
for each vertex u G->V
{
if (u->color == WHITE)
DFS_Visit(u);
}
}
DFS_Visit(u)
{
u->color = GREY;
time = time+1;
u->d = time;
for each v u->Adj[]
{
if (v->color == WHITE)
DFS_Visit(v);
}
u->color = BLACK;
time = time+1;
u->f = time;
}
What does u->f represent?
Depth-First Search: The Code
DFS(G)
{
for each vertex u G->V
{
u->color = WHITE;
}
time = 0;
for each vertex u G->V
{
if (u->color == WHITE)
DFS_Visit(u);
}
}
DFS_Visit(u)
{
u->color = GREY;
time = time+1;
u->d = time;
for each v u->Adj[]
{
if (v->color == WHITE)
DFS_Visit(v);
}
u->color = BLACK;
time = time+1;
u->f = time;
}
Will all vertices eventually be colored black?
Depth-First Search: The Code
DFS(G)
{
for each vertex u G->V
{
u->color = WHITE;
}
time = 0;
for each vertex u G->V
{
if (u->color == WHITE)
DFS_Visit(u);
}
}
DFS_Visit(u)
{
u->color = GREY;
time = time+1;
u->d = time;
for each v u->Adj[]
{
if (v->color == WHITE)
DFS_Visit(v);
}
u->color = BLACK;
time = time+1;
u->f = time;
}
What will be the running time?
Depth-First Search: The Code
DFS(G)
{
for each vertex u G->V
{
u->color = WHITE;
}
time = 0;
for each vertex u G->V
{
if (u->color == WHITE)
DFS_Visit(u);
}
}
DFS_Visit(u)
{
u->color = GREY;
time = time+1;
u->d = time;
for each v u->Adj[]
{
if (v->color == WHITE)
DFS_Visit(v);
}
u->color = BLACK;
time = time+1;
u->f = time;
}
Running time: O(n2) because call DFS_Visit on each vertex,
and the loop over Adj[] can run as many as |V| times
Depth-First Search: The Code
DFS(G)
{
for each vertex u G->V
{
u->color = WHITE;
}
time = 0;
for each vertex u G->V
{
if (u->color == WHITE)
DFS_Visit(u);
}
}
DFS_Visit(u)
{
u->color = GREY;
time = time+1;
u->d = time;
for each v u->Adj[]
{
if (v->color == WHITE)
DFS_Visit(v);
}
u->color = BLACK;
time = time+1;
u->f = time;
}
BUT, there is actually a tighter bound.
How many times will DFS_Visit() actually be called?
Depth-First Search: The Code
DFS(G)
{
for each vertex u G->V
{
u->color = WHITE;
}
time = 0;
for each vertex u G->V
{
if (u->color == WHITE)
DFS_Visit(u);
}
}
DFS_Visit(u)
{
u->color = GREY;
time = time+1;
u->d = time;
for each v u->Adj[]
{
if (v->color == WHITE)
DFS_Visit(v);
}
u->color = BLACK;
time = time+1;
u->f = time;
}
So, running time of DFS = O(V+E)
Depth-First Sort Analysis
This running time argument is an informal
example of amortized analysis
Charge the exploration of edge to the edge:
Each loop in DFS_Visit can be attributed to an edge in the
graph
Runs once/edge if directed graph, twice if undirected
Thus loop will run in O(E) time, algorithm O(V+E)
Considered linear for graph, b/c adj list requires O(V+E) storage
Important to be comfortable with this kind of reasoning
and analysis
DFS Example
source
vertex
DFS Example
source
vertex
1 |
DFS Example
source
vertex
1 |
2 |
DFS Example
source
vertex
d
1 |
f
|
2 |
3 |
DFS Example
source
vertex
1 |
2 |
3 | 4
DFS Example
source
vertex
1 |
2 |
3 | 4
5 |
DFS Example
source
vertex
1 |
2 |
3 | 4
5 | 6
DFS Example
source
vertex
1 |
8 |
2 | 7
3 | 4
5 | 6
DFS Example
source
vertex
1 |
8 |
2 | 7
3 | 4
5 | 6
DFS Example
source
vertex
1 |
8 |
2 | 7
9 |
3 | 4
5 | 6
What is the structure of the grey vertices?
What do they represent?
DFS Example
source
vertex
1 |
8 |
2 | 7
9 |10
3 | 4
5 | 6
DFS Example
source
vertex
1 |
8 |11
2 | 7
9 |10
3 | 4
5 | 6
DFS Example
source
vertex
1 |12
8 |11
2 | 7
9 |10
3 | 4
5 | 6
DFS Example
source
vertex
1 |12
8 |11
2 | 7
13|
9 |10
3 | 4
5 | 6
DFS Example
source
vertex
1 |12
8 |11
2 | 7
13|
9 |10
3 | 4
5 | 6
14|
DFS Example
source
vertex
1 |12
8 |11
2 | 7
13|
9 |10
3 | 4
5 | 6
14|15
DFS Example
source
vertex
1 |12
8 |11
2 | 7
13|16
9 |10
3 | 4
5 | 6
14|15
DFS: Kinds of edges
DFS introduces an important distinction
among edges in the original graph:
Tree edge: encounter new (white) vertex
The tree edges form a spanning forest
Can tree edges form cycles? Why or why not?
DFS Example
source
vertex
1 |12
8 |11
2 | 7
13|16
9 |10
3 | 4
Tree edges
5 | 6
14|15
DFS: Kinds of edges
DFS introduces an important distinction
among edges in the original graph:
Tree edge: encounter new (white) vertex
Back edge: from descendent to ancestor
Encounter a grey vertex (grey to grey)
DFS Example
source
vertex
1 |12
8 |11
2 | 7
13|16
9 |10
3 | 4
Tree edges Back edges
5 | 6
14|15
DFS: Kinds of edges
DFS introduces an important distinction
among edges in the original graph:
Tree edge: encounter new (white) vertex
Back edge: from descendent to ancestor
Forward edge: from ancestor to descendent
Not a tree edge, though
From grey node to black node
DFS Example
source
vertex
1 |12
8 |11
2 | 7
13|16
9 |10
3 | 4
5 | 6
Tree edges Back edges Forward edges
14|15
DFS: Kinds of edges
DFS introduces an important distinction
among edges in the original graph:
Tree edge: encounter new (white) vertex
Back edge: from descendent to ancestor
Forward edge: from ancestor to descendent
Cross edge: between a tree or subtrees
From a grey node to a black node
DFS Example
source
vertex
1 |12
8 |11
2 | 7
13|16
9 |10
3 | 4
5 | 6
14|15
Tree edges Back edges Forward edges Cross edges
DFS: Kinds of edges
DFS introduces an important distinction
among edges in the original graph:
Tree edge: encounter new (white) vertex
Back edge: from descendent to ancestor
Forward edge: from ancestor to descendent
Cross edge: between a tree or subtrees
Note: tree & back edges are important; most
algorithms dont distinguish forward & cross
DFS: Kinds Of Edges
Thm 23.9: If G is undirected, a DFS produces
only tree and back edges
Proof by contradiction:
Assume theres a forward edge
But F? edge must actually be a
back edge (why?)
source
F?
DFS: Kinds Of Edges
Thm 23.9: If G is undirected, a DFS produces only
tree and back edges
Proof by contradiction:
Assume theres a cross edge
source
But C? edge cannot be cross:
must be explored from one of the
vertices it connects, becoming a tree
vertex, before other vertex is explored
So in fact the picture is wrongboth
lower tree edges cannot in fact be
tree edges
C?
DFS And Graph Cycles
Thm: An undirected graph is acyclic iff a DFS yields
no back edges
If acyclic, no back edges (because a back edge implies a
cycle
If no back edges, acyclic
No back edges implies only tree edges (Why?)
Only tree edges implies we have a tree or a forest
Which by definition is acyclic
Thus, can run DFS to find whether a graph has a cycle
DFS And Cycles
How would you modify the code to detect cycles?
DFS(G)
{
for each vertex u G->V
{
u->color = WHITE;
}
time = 0;
for each vertex u G->V
{
if (u->color == WHITE)
DFS_Visit(u);
}
}
DFS_Visit(u)
{
u->color = GREY;
time = time+1;
u->d = time;
for each v u->Adj[]
{
if (v->color == WHITE)
DFS_Visit(v);
}
u->color = BLACK;
time = time+1;
u->f = time;
}
DFS And Cycles
What will be the running time?
DFS(G)
{
for each vertex u G->V
{
u->color = WHITE;
}
time = 0;
for each vertex u G->V
{
if (u->color == WHITE)
DFS_Visit(u);
}
}
DFS_Visit(u)
{
u->color = GREY;
time = time+1;
u->d = time;
for each v u->Adj[]
{
if (v->color == WHITE)
DFS_Visit(v);
}
u->color = BLACK;
time = time+1;
u->f = time;
}
DFS And Cycles
What will be the running time?
A: O(V+E)
We can actually determine if cycles exist in
O(V) time:
In an undirected acyclic forest, |E| |V| - 1
So count the edges: if ever see |V| distinct edges,
must have seen a back edge along the way