Graphs Chapter 15 explain graph-based algorithms Graph definitions Graph implementations. Graph Traversals The presentation illustrates two quadratic sorting algorithms: Selectionsort and Insertionsort. Before this lecture, students should know about arrays, and should have seen some motivation for sorting (such as binary search of a sorted array). Data Structures and Other Objects Using C++
Graph Representation A graph G = (V, E) V = set of vertices E = set of edges = subset of V V Thus |E| = O(|V|2)
Graph Variations Variations: A connected graph has a path from every vertex to every other In an undirected graph: Edge (u,v) = edge (v,u) No self-loops In a directed graph: Edge (u,v) goes from vertex u to vertex v, notated uv
Graph Variations More variations: A weighted graph associates weights with either the edges or the vertices e.g., a road map: edges might be weighted with distance A hypergraph allows multiple edges between the same vertices e.g., the call graph in a program (a function can get called from multiple points in another function)
Graphs We will typically express running times in terms of |E| and |V| If |E| |V|2 the graph is dense If |E| |V| the graph is sparse If you know you are dealing with dense or sparse graphs, different data structures may make sense
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 = 0, if edge (i, j) E A[i, j] = weight on the edge, if edge (i, j) E = 0, if edge (i, j) E
Graphs: Adjacency Matrix Example: A 1 2 3 4 ?? 1 a 2 d 4 b c 3
Graphs: Adjacency Matrix Example: A 1 2 3 4 1 a 2 d 4 b c 3 How much storage does the adjacency matrix require? A: O(V2)
Graphs: Adjacency Matrix The adjacency matrix is a dense representation Usually too much storage for large graphs But can be very efficient for small graphs Most large interesting graphs are sparse e.g., planar graphs, in which no edges cross, have |E| = O(|V|) by Euler’s formula For this reason the adjacency list is often a more appropriate representation
Graphs: Adjacency List Adjacency list: for each vertex v V, store a list of vertices adjacent to v Example: For an undirected graph
Graphs: Adjacency List Adjacency list: for each vertex v V, store a list of vertices adjacent to v Example: For a directed graph
Graphs: Adjacency List How much storage is required? The degree of a vertex v = # of edges Directed graphs have in-degree, out-degree For directed graphs, # of items in adjacency lists is out-degree(v) = |E| takes O(V + E) storage For undirected graphs, # items in adjacency lists is degree(v) = 2 |E| (handshaking lemma) also O(V + E) storage So: Adjacency lists take O(V+E) storage
Graph Traverse 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
Depth-First Search (DFS) “Explore” a graph, turning it into a tree One vertex at a time Moving “deeper” from last visited vertex to unvisited one Backtracks if no adjacent unvisited vertex Uses a stack A vertex is pushed onto the stack when it’s reached for the first time A vertex is popped off the stack when it become a dead end, i.e., when there is no adjacent unvisited vertex
Example: DFS
Example: DFS a b e f c d g h DFS tree: a b DFS traversal stack: f g
DFS: Kinds of edges Tree edge Back edge: from descendent to ancenstor Forward edge: from ancenstor to descendent Cross edge: between subtress
Notes on DFS DFS can be implemented with graphs represented as: adjacency matrices: Θ(V2) adjacency lists: Θ(|V|+|E|) Yields two distinct ordering of vertices: order in which vertices are first encountered (pushed onto stack) order in which vertices become dead-ends (popped off stack) Applications: checking connectivity, finding connected components checking acyclicity finding articulation points and biconnected components searching state-space of problems for solution (AI)
Breadth-first search (BFS) Visits graph vertices by moving across to all the neighbors of last visited vertex Instead of a stack, BFS uses a queue Similar to level-by-level tree traversal
Example: BFS
Example: BFS BFS traversal queue: a1 b2 e3 f4 g5 c6 h7 d8 a b e f c d BFS tree: a b e f g c h d BFS traversal queue: a1 b2 e3 f4 g5 c6 h7 d8
Notes on BFS BFS has same efficiency as DFS and can be implemented with graphs represented as: adjacency matrices: Θ(V2) adjacency lists: Θ(|V|+|E|) Yields single ordering of vertices (order added/deleted from queue is the same) Applications: same as DFS, but can also find paths from a vertex to all other vertices with the smallest number of edges
Directed Acyclic Graphs A directed acyclic graph or DAG is a directed graph with no directed cycles:
DAG Shortest Paths Problem: finding shortest paths in DAG
Dijkstra’s Algorithm If no negative edge weights, Dijkstra’s essentially a weighted version of breadth-first search Similar to breadth-first search Grow a tree gradually, advancing from vertices taken from a queue
Dijkstra’s Algorithm 10 4 3 2 1 5 A B C D Dijkstra(G) for each v V d[v] = ; d[s] = 0; S = ; Q = V; while (Q ) u = ExtractMin(Q); S = S U {u}; for each v u->Adj[] if (d[v] > d[u]+w(u,v)) d[v] = d[u]+w(u,v);
Dijkstra’s Algorithm B Dijkstra(G) for each v V d[v] = ; d[s] = 0; S = ; Q = V; while (Q ) u = ExtractMin(Q); S = S U {u}; for each v u->Adj[] if (d[v] > d[u]+w(u,v)) d[v] = d[u]+w(u,v); 2 s A 10 D 4 3 5 1 C
Dijkstra’s Algorithm B Dijkstra(G) for each v V d[v] = ; d[s] = 0; S = ; Q = V; while (Q ) u = ExtractMin(Q); S = S U {u}; for each v u->Adj[] if (d[v] > d[u]+w(u,v)) d[v] = d[u]+w(u,v); 10 2 s A 10 D 4 3 5 1 5 C S = {A}
Dijkstra’s Algorithm B Dijkstra(G) for each v V d[v] = ; d[s] = 0; S = ; Q = V; while (Q ) u = ExtractMin(Q); S = S U {u}; for each v u->Adj[] if (d[v] > d[u]+w(u,v)) d[v] = d[u]+w(u,v); 9 2 s A 10 D 4 3 6 5 1 5 C S = {A, C}
Dijkstra’s Algorithm B Dijkstra(G) for each v V d[v] = ; d[s] = 0; S = ; Q = V; while (Q ) u = ExtractMin(Q); S = S U {u}; for each v u->Adj[] if (d[v] > d[u]+w(u,v)) d[v] = d[u]+w(u,v); 8 2 s A 10 D 4 3 6 5 1 5 C S = {A, C, D}
Dijkstra’s Algorithm B Dijkstra(G) for each v V d[v] = ; d[s] = 0; S = ; Q = V; while (Q ) u = ExtractMin(Q); S = S U {u}; for each v u->Adj[] if (d[v] > d[u]+w(u,v)) d[v] = d[u]+w(u,v); 8 2 s A 10 D 4 3 6 5 1 5 C S = {A, C, D, B}
Dijkstra’s Algorithm B Dijkstra(G) for each v V d[v] = ; d[s] = 0; S = ; Q = V; while (Q ) u = ExtractMin(Q); S = S U {u}; for each v u->Adj[] if (d[v] > d[u]+w(u,v)) d[v] = d[u]+w(u,v); 8 2 s A 10 D 4 3 6 5 1 5 C
Dijkstra’s Algorithm Analyasis How many times is ExtractMin() called? Dijkstra(G) for each v V d[v] = ; d[s] = 0; S = ; Q = V; while (Q ) u = ExtractMin(Q); S = S U {u}; for each v u->Adj[] if (d[v] > d[u]+w(u,v)) d[v] = d[u]+w(u,v); A: O(E lg V) using heap for Q What will be the total running time?