Graph Algorithms

Topics

Seven Bridges of Königsberg

                       

Can one walk the city crossing every bridge once and only once?

Euler answered "No." Why?
View the land as vertices. The bridges are edges. There is an Eulerian walk on a graph only if it is connected and has either zero or two edges of odd degree.

Graph theory was born to solve a problem of movement in space.

But it is also used for:

Elements:

Representations of graphs



There are two standard representations:

  1. Adjancency lists
  2. Adjancency matrices

Consider the following graph:



Following our text, we will prefer adjancency lists. But, as CLRS point out, in an especially dense graph, or when we need to detect an edge quickly, we might prefer a matrix.

Here are the two different representations of this graph:

Adjacency list:


Adjacency matrix:


For the adjancency matrix:



Trade-offs

Both forms can be used to represent directed, undirected, and weighted graphs.
For weighted graphs, we can store 0 or the weight in the matrix instead of just 0 or 1. For the list, we store a tuple (vertex, weight) in the adjancency list of a vertex.
For directed graphs, in a list, i, say, would have an entry for j, but j would not for i. In a matrix, m[i][j] would be 1, but m[j][i] would be 0.

Representing attributes

For pseudo-code, we just represent attribute f of edge (u, v) as (u, v).f. (f might represent the edge already having been visited, for instance.)

In a real program, there are many, many ways to store additional information. How to best do this will very much depend on your application. I have found this can work:

Breadth-first search



We assume a source vertex s.
We then find every vertex at distance 1 from the source. (Connected by a direct edge.)
Then we process those vertices, finding every vertex at distance 2 from the source vertex.
We continue in the same fashion until we run out of vertices.

Coloring vertices:
Vertices start out "white."
They are colored gray when they are discovered.
They are colored black when all of their adjacent vertices have been discovered.

Analysis

Shortest paths

Breadth-first search computes shortest path distances.

Depth-first search



This search goes as "deep" as it can before it ventures back up the graph to explore other nodes nearer the source.

Coloring vertices:
Vertices start out "white."
They are colored gray when they are discovered.
They are colored black when they are "finished," meaning when all of the nodes on their adjacency list have been completely explored.

Properties of depth-first search

Running time: O(V + E)

Classification of edges

Types of edges:

  1. Tree edges:
    Edges of the depth-first forest Gπ. Edge (u, v) is a tree edge if v was first discovered by exploring edge (u, v).
  2. Back edges:
    An edge (u, v) that connects u to an ancestor v.
  3. Forward edges:
    Non-tree edge (u, v) that connects u to an descendant v.
  4. Cross edges:
    All other edges.

In DFS, when we first explore (u, v) the color of v tells us:

  1. WHITE: This is a tree edge.
  2. GRAY: This is a back edge.
  3. BLACK: This is a forward edge or cross edge.

Topological sort



Can only be performed on directed acyclical graphs (DAGs). The sort makes no sense on undirected graphs or cyclical graphs.

Property: If G contains an edge (u, v), then u appears before v in the topological ordering.

Our book's topological sort algorithm is somewhat weird:
TOPOLOGICAL-SORT(G)

  1. call DFS(G) to compute finishing times v.f for each vertex v.
  2. as each vertex is finished, insert it into the front of a linked list
  3. return the linked list of vertices.

Strongly connected components



These are components of directed graphs that can each be reached from the other.
Many advanced graph algorithms rely on de-composing a graph into strongly directed components, and then processing those, and then combining the results: divide-and-conquer!

Source Code

Python
Ruby

For Further Study

Homework

  1. Master Theorem problems:
    1. T(n) = .2T(n / 2) + 5n
    2. T(n) = 32T(n / 8) + n!
    3. T(n) = √2T(n / 2) + log n
    4. T(n) = 7/2T(n / 2) + n
    5. T(n) = 4T(n / 4) + √n
    6. T(n) = 23T(n / 7) - n2 log n
  2. Work out a better implementation of the graph data structure than I did in the Python code linked to above.
  3. Consider the following graph represented as an adjacency list:

    Show the d and π values that result from running BFS on that graph.
  4. Now do the same thing with the above graph for DFS.
  5. Do problem 22.1-6 in the textbook.
  6. Read Chapter 23, section on minimum spanning trees.

Credits