28. Graphs
Definitions
- vertices / nodes
- edges / arcs
- directed, undirected
- path (length = #edges)
- strong connected (connected for undirected graphs)
- degree: the number of edges incident on that vertex.
Graph Representations
Adjacency matrix
A |V|-by-|V| array of boolean values (where |V| is the number of vertices in the graph). Each row and column represents a vertex of the graph. Set the value at row i, column j to true if (i, j) is an edge of the graph. If the graph is undirected (below right), the adjacency matrix is symmetric: row i, column j has the same value as row j, column i.
1 2 3 4 5 6 Alb Ken Eme Ber Oak Pie
1 - - - T - - Albany - T - T - -
2 T - - - - - Kensington T - - T - -
3 - T - - - T Emeryville - - - T T -
4 - - - - T - Berkeley T T T - T -
5 - T - - - T Oakland - - T T - T
6 - - T - - - Piedmont - - - - T -
- Very wasteful for a sparse graph.
Adjacency list
Each vertex maintains a list of the edges directed out from v.
--- ----- --- ------ ------
1 |.+-> | 4 | Albany |.+-> |Ken.+-> |Ber*|
--- ===== === ====== ======
2 |.+-> | 1 | Kensington |.+-> |Alb.+-> |Ber*|
--- =====---- === ====== ======
3 |.+-> | 2 | 6 | Emeryville |.+-> |Ber.+-> |Oak*|
--- =====---- === ====== ====== ------ ------
4 |.+-> | 5 | Berkeley |.+-> |Alb.+-> |Ken.+-> |Eme.+-> |Oak*|
--- =====---- === ====== ====== ====== ------
5 |.+-> | 2 | 6 | Oakland |.+-> |Eme.+-> |Ber.+-> |Pie*|
--- =====---- === ====== ------ ------
6 |.+-> | 3 | Piedmont |.+-> |Oak*|
--- ----- --- ------
The total memory used by all the lists is Theta(|V| + |E|).
An adjacency list is more space- and time-efficient than an adjacency matrix for a sparse graph, but less efficient for a complete graph. A complete graph is a graph having every possible edge; that is, for every vertex u and every vertex v, (u, v) is an edge of the graph.
Graph Traversals
- DFS
- BFS (if your graph is an undirected tree, it is a level-order tree traversal)
Unlike a tree, there may be several ways to get from one vertex to another. Therefore, each vertex has a boolean field called "visited" that tells us if we have visited the vertex before.
DFS
Algorithm
public class Graph {
public void dfs(Vertex u) {
u.visit();
u.visited = true;
for (each vertex v such that (u, v) is an edge in E) {
if (!v.visited) {
dfs(v);
}
}
}
}
Running Time
- adjacency list: O(|V| + |E|)*
- adjacency matrix: O(|V|^2)
Hence, an adjacency list is asymptotically faster if the graph is sparse.
Application
Determine whether there is a path from a vertex u to another vertex v.
Why does DFS on an adjacency list run in O(|V|+|E|) time?
O(|V|) - solely because we have to initialize all the "visited" flags.
O(|E|) - how many times does the "for" loop iterate?
D = sum d(v). || d(v) - outdegree of v
v in V
How large is D?
- If G is a directed graph, then D = |E|, the number of edges
- If G is an undirected graph with no self-edges, then D = 2 |E|, because each edge offers a path out of two vertices.
- If G is undirected graph with one or more self-edges, then D < 2 |E|.