CS 261 – Data Structures Graphs. Used in a variety of applications and algorithms Graphs represent...
-
date post
20-Dec-2015 -
Category
Documents
-
view
218 -
download
1
Transcript of CS 261 – Data Structures Graphs. Used in a variety of applications and algorithms Graphs represent...
CS 261 – Data Structures
Graphs
Graphs• Used in a variety of applications and algorithms
• Graphs represent relationships or connections
• Superset of trees (i.e., a tree is a restricted form of a graph):– A graph represents general relationships:
• Each node may have many predecessors
• There may be multiple paths (or no path) from one node to another
• Can have cycles or loops
• Examples: airline flight connections, friends, algorithmic flow, etc.
– A tree has more restrictive relationships and topology:• Each node has a single predecessor—its parent
• There is a single, unique path from the root to any node
• No cycles
• Example: less than or greater than in a binary search tree
Graphs: Vertices and Edges• A graph is composed of vertices and edges
• Vertices (also called nodes):– Represent objects, states (i.e., conditions or configurations), positions,
or simply just place holders– Set {v1, v2, …, vn}: each vertex is unique no two vertices represent the
same object/state
• Edges (also called arcs):– Can be either directed or undirected– Can be either weighted (or labeled) or unweighted
– An edge (vi, vj) between two vertices indicates that they are directly related, connected, etc.
– If there is an edge from vi to vj, then vj is a neighbor of vi (if the edge is undirected then vi and vj are neighbors or each other)
Graphs: Types of Edges
Undirected Directed
Unweighted
Weighted
v1
v2
v4v3
v1
v2
v4v3
v1
v2
v4v3
w1,2
w2,3
w1,3
w3,4
w4,1v1
v2
v4v3
w1-2
w2-3
w1-3
w3-4
w1-4
Graphs: Directed and Undirected
• An undirected edge e = (vi, vj) indicates that the relationship, connection, etc. is bi-direction:– Can go from vi to vj (i.e., vi is related to vj) and vice-versa
– Example: friends – Steve and Alicia are friends
• A directed edge = (vi, vj) specifies a one-directional relationship or connection:– Can only go from vi to vj
– Example: like – George likes Mary
Steve Alicia
George Mary
er
Graphs: Directed and Undirected (cont.)
• A graph will have either directed or undirected edges, but typically not both.
• Two directed edges can replace any undirected edge:
– If there is an undirected edge e = (vi, vj), it is replaced with two directed edges = (vi, vj) and = (vj, vi)
– If the graph is unweighted (or the weights for both directed edges are
the same), then a shorthand (graphical) notation can be used
– We will discuss only directed graphs (and use e instead of )
er
er
vi vjvi vj
vi vj vi vj
er
Graphs: Example
Pendleton
Pierre
Pensacola
Princeton
Pittsburgh
Peoria
Pueblo
Phoenix
Graphs: Representations
Two most common representations:1. Adjacency matrix:
• Represents graph as a 2-D matrix• Vertices are used as indices for both the rows and columns of the matrix
– Vertices must be numbered or must associate an integer value with each vertex
• A matrix entry in row i and column j of:1: indicates an edge from vi to vj
0: no edge from vi to vj
• Weighted representation has weight wi,j instead of 1 and instead of 0
• Requires O(V 2) space for V vertices
2. Edge list:• Each vertex vi lists the set of directly connected neighbors
• Weighted representation replaces the neighbor set with a Map:–key vertex vj
–value weight wi,j
• Stores only the edges more space efficient for sparse graph: O(V + E)
Graphs Representation: Adjacency Matrix
City 0 1 2 3 4 5 6 7
0: Pendleton ? 0 0 1 0 0 0 1
1: Pensacola 0 ? 0 1 0 0 0 0
2: Peoria 0 0 ? 0 0 1 0 1
3: Phoenix 0 0 1 ? 0 1 0 1
4: Pierre 1 0 0 0 ? 0 0 0
5: Pittsburgh 0 1 0 0 0 ? 0 0
6: Princeton 0 0 0 0 0 1 ? 0
7: Pueblo 0 0 0 0 1 0 0 ?
Pendleton
Pierre
Pensacola
Princeton
Pittsburgh
Peoria
Pueblo
Phoenix
What about the diagonal matrix entries?
Is a vertex connected to itself?
Graphs Representation: Adjacency Matrix
City 0 1 2 3 4 5 6 7
0: Pendleton 1 0 0 1 0 0 0 1
1: Pensacola 0 1 0 1 0 0 0 0
2: Peoria 0 0 1 0 0 1 0 1
3: Phoenix 0 0 1 1 0 1 0 1
4: Pierre 1 0 0 0 1 0 0 0
5: Pittsburgh 0 1 0 0 0 1 0 0
6: Princeton 0 0 0 0 0 1 1 0
7: Pueblo 0 0 0 0 1 0 0 1
Pendleton
Pierre
Pensacola
Princeton
Pittsburgh
Peoria
Pueblo
Phoenix
By convention, a vertex is usually connected to itself(though, this is not always the case)
Graphs Representation: Edge List
Pendleton
Pierre
Pensacola
Princeton
Pittsburgh
Peoria
Pueblo
Phoenix
Pendleton: {Pueblo, Phoenix}Pensacola: {Phoenix}Peoria: {Pueblo, Pittsburgh}Phoenix: {Pueblo, Peoria, Pittsburgh}Pierre: {Pendleton}Pittsburgh: {Pensacola}Princeton: {Pittsburgh}Pueblo: {Pierre}
Reachability• A common question to ask about a graph is reachability:
• Single-source:– Which vertices are “reachable” from a given vertex vi?
– Basic algorithm:Initialize set of reachable vertices with vi and add vi to a stack
While stack is not empty
Get and remove (pop) last vertex v from stack
For all neighbors, vj, of v
If vj is not is set of reachable vertices, add to stack and reachable set
• All-pairs:– For all pairs of vertices vi and vj, is vj “reachable” from vi?
– Solves the single source question for all vertices
All-Pairs Reachability: Adjacency Matrix
• How do we compute all-pairs reachability
• Converts adjacency matrix into a “reachability” matrix– A matrix entry in row i and column j of:
1: indicates that there is a path (of zero or more edges) from vi to vj
0: no path from vi to vj
• Warshall’s algorithm:– Named after the computer scientist who discovered it
– Three nested loops of order V O(V 3)
– Key idea: each iteration of the outer loop (index k) adds any path of length 2 that has vertex vk as its center
– Since the adjacency and the reachability matrices are binary (0 or
1 entries), use bitwise operations
Warshall’s Algorithmclass Warshall {
static void warshall(int [][] a) { // Input: initial adjacency matrix. int n = a.length; // Number of vertices.
for (int k = 0; k < n; k++) { // Add paths of length 2 through vk. for (int i = 0; i < n; i++) for (int j = 0; j < n; j++) // Add path from vi to vj a[i][j] |= a[i][k] & a[k][j]; // going through vk. } }
static void matrixOutput(int [][] a) { // Print matrix. for (int i = 0; i < a.length; i++) { for (int j = 0; j < a[i].length; j++) System.out.print(" " + a[i][j]); System.out.println(" "); } } ... }
Warshall’s Algorithm: Initializationclass Warshall { ...
static public void main (String [] args) { int [][] adjacency = {{1, 0, 0, 1, 0, 0, 0, 1}, // Initialize {0, 1, 0, 1, 0, 0, 0, 0}, // adjacency {0, 0, 1, 0, 0, 1, 0, 1}, // matrix. {0, 0, 1, 1, 0, 1, 0, 1}, {1, 0, 0, 0, 1, 0, 0, 0}, {0, 1, 0, 0, 0, 1, 0, 0}, {0, 0, 0, 0, 0, 1, 1, 0}, {0, 0, 0, 0, 1, 0, 0, 1}};
warshall (adjacency); // Compute all-pairs reachability matrix. matrixOutput(adjacency); // Print resulting reachability matrix. } }
Warshall’s Algorithm: Initialization
City 0 1 2 3 4 5 6 7
0: Pendleton 1 0 0 1 0 0 0 1
1: Pensacola 0 1 0 1 0 0 0 0
2: Peoria 0 0 1 0 0 1 0 1
3: Phoenix 0 0 1 1 0 1 0 1
4: Pierre 1 0 0 0 1 0 0 0
5: Pittsburgh 0 1 0 0 0 1 0 0
6: Princeton 0 0 0 0 0 1 1 0
7: Pueblo 0 0 0 0 1 0 0 1
Pendleton
Pierre
Pensacola
Princeton
Pittsburgh
Peoria
Pueblo
Phoenix
Initial adjacency matrix
Warshall’s Algorithm: After Iteration 0
City 0 1 2 3 4 5 6 7
0: Pendleton 1 0 0 1 0 0 0 1
1: Pensacola 0 1 0 1 0 0 0 0
2: Peoria 0 0 1 0 0 1 0 1
3: Phoenix 0 0 1 1 0 1 0 1
4: Pierre 1 0 0 1 1 0 0 1
5: Pittsburgh 0 1 0 0 0 1 0 0
6: Princeton 0 0 0 0 0 1 1 0
7: Pueblo 0 0 0 0 1 0 0 1
Pendleton
Pierre
Pensacola
Princeton
Pittsburgh
Peoria
Pueblo
Phoenix
Add all paths of length 2 that go through vertex 0 (Pendleton)
Define some paths of length 2 in this (the first) iteration
Warshall’s Algorithm: After Iteration 1
City 0 1 2 3 4 5 6 7
0: Pendleton 1 0 0 1 0 0 0 1
1: Pensacola 0 1 0 1 0 0 0 0
2: Peoria 0 0 1 0 0 1 0 1
3: Phoenix 0 0 1 1 0 1 0 1
4: Pierre 1 0 0 1 1 0 0 1
5: Pittsburgh 0 1 0 1 0 1 0 0
6: Princeton 0 0 0 0 0 1 1 0
7: Pueblo 0 0 0 0 1 0 0 1
Add all paths of length 2 that go through vertex 1 (Pensacola)
Pendleton
Pierre
Pensacola
Princeton
Pittsburgh
Peoria
Pueblo
Phoenix
Could potentially define paths of length 3 in this (the second) iteration
Warshall’s Algorithm: After Iteration 2
City 0 1 2 3 4 5 6 7
0: Pendleton 1 0 0 1 0 0 0 1
1: Pensacola 0 1 0 1 0 0 0 0
2: Peoria 0 0 1 0 0 1 0 1
3: Phoenix 0 0 1 1 0 1 0 1
4: Pierre 1 0 0 1 1 0 0 1
5: Pittsburgh 0 1 0 1 0 1 0 0
6: Princeton 0 0 0 0 0 1 1 0
7: Pueblo 0 0 0 0 1 0 0 1
Add all paths of length 2 that go through vertex 2 (Peoria)
Pendleton
Pierre
Pensacola
Princeton
Pittsburgh
Peoria
Pueblo
Phoenix
Edges already exist (doesn’t add anything new)
Could potentially define paths of length 4 in this (the third) iteration
Warshall’s Algorithm: After Iteration 3
City 0 1 2 3 4 5 6 7
0: Pendleton 1 0 1 1 0 1 0 1
1: Pensacola 0 1 1 1 0 1 0 1
2: Peoria 0 0 1 0 0 1 0 1
3: Phoenix 0 0 1 1 0 1 0 1
4: Pierre 1 0 1 1 1 1 0 1
5: Pittsburgh 0 1 1 1 0 1 0 1
6: Princeton 0 0 0 0 0 1 1 0
7: Pueblo 0 0 0 0 1 0 0 1
Add all paths of length 2 that go through vertex 3 (Phoenix)
Pendleton
Pierre
Pensacola
Princeton
Pittsburgh
Peoria
Pueblo
Phoenix
Notice that it also includes the paths formed in interations 0 and 1 (resulting, in this case, in new reachability paths of length 3)
Could potentially define paths of length 5 in this (the fourth) iteration
Warshall’s Algorithm: After Iteration 7
City 0 1 2 3 4 5 6 7
0: Pendleton 1 1 1 1 1 1 0 1
1: Pensacola 1 1 1 1 1 1 0 1
2: Peoria 1 1 1 1 1 1 0 1
3: Phoenix 1 1 1 1 1 1 0 1
4: Pierre 1 1 1 1 1 1 0 1
5: Pittsburgh 1 1 1 1 1 1 0 1
6: Princeton 1 1 1 1 1 1 1 1
7: Pueblo 1 1 1 1 1 1 0 1
Final result: Every city can reach every other city, except for Princeton
Pendleton
Pierre
Pensacola
Princeton
Pittsburgh
Peoria
Pueblo
Phoenix
The final matrix has a 1 in row i and column j if vertex vj is reachable from vertex vi via some path
Warshall’s Algorithm: Another Look
static void warshall(int [][] a) { // Input: initial adjacency matrix. int n = a.length; // Number of vertices.
for (int k = 0; k < n; k++) { // Add paths of length 2 through vk. for (int i = 0; i < n; i++) for (int j = 0; j < n; j++) // Add path from vi to vj a[i][j] |= a[i][k] & a[k][j]; // going through vk. } }
• Notice that a[i][k] doesn’t change inside the inner loop:– If zero, then the inner loop does nothing
– If one, don’t need to perform bitwise AND (&)
– If graph is sparse, the inner loop is not needed very often (at least initially)
– May get a performance improvement by checking a[i][k] prior to third loop
Warshall’s Algorithm: Another Look (cont.)
Improved code:
static void warshall(int [][] a) { // Input: initial adjacency matrix. int n = a.length; // Number of vertices.
for (int k = 0; k < n; k++) { // Add paths of length 2 through vk. for (int i = 0; i < n; i++) if (a[i][k] == 1) // If there is a path from vi to vk: for (int j = 0; j < n; j++) // Add path from vi to vj a[i][j] |= a[k][j]; // going through vk. } }
Weighted Graph: All-Pairs Reachability
• What if we have a weighted graph?– Example: the distance between cities
• The all-pairs reachability question then becomes:– Is there a path between any two vertices vi and vk and, if so, what
is weight of the minimum weight path?
• Floyds’s algorithm:– Named after the computer scientist who discovered it
– Very much the same as Warshall’s algorithm
– Key difference: adjacency graph now has weights instead of binary values• In place of bitwise AND, use addition
• In place of bitwise OR, use a minimum-value calculation
Floyds’s Algorithm: Initialization
City 0 1 2 3 4 5 6 7
0: Pendleton 0 4 8
1: Pensacola 0 5
2: Peoria 0 5 3
3: Phoenix 4 0 10 3
4: Pierre 2 0
5: Pittsburgh 4 0
6: Princeton 1 0
7: Pueblo 3 0
Pendleton
Pierre
Pensacola
Princeton
Pittsburgh
Peoria
Pueblo
Phoenix
Initial adjacency matrix
2
4
3
3
4
3
5
410
8
5
2
Floyd’s Algorithmclass Floyd {
static void floyd(double [][] a) { // Input: initial adjacency matrix. int n = a.length; // Number of vertices.
for (int k = 0; k < n; k++) { // Add paths of length 2 through vk. for (int i = 0; i < n; i++) for (int j = 0; j < n; j++) { // Check path from vi to vj double m = a[i][k] + a[k][j]; // going through vk. if (m < a[i][j]) a[i][j] = m; // Update if less. } } } ... }
Floyd’s Algorithm: Improved
Of course, we can apply a similar improvement as we did to Warshall’s algorithmclass Floyd {
static void floyd(double [][] a) { // Input: initial adjacency matrix. int n = a.length; // Number of vertices.
for (int k = 0; k < n; k++) { // Add paths of length 2 through vk. for (int i = 0; i < n; i++) { double m = a[i][k]; // If there is a path from vi to vk: if (m < Double.POSITIVE_INFINITY) for (int j = 0; j < n; j++) { // Check path from vi to vj double m += a[k][j]; // going through vk. if (m < a[i][j]) a[i][j] = m; // Update if less. } } } } ... }
Single Source Reachability: Edge-List
• How do you determine reachability with an edge-list representation?– In this case we want to answer the single-source question:
What is reachable from a given vertex vi?
– Should be faster than the all-pairs question
– Basic algorithm:Initialize set of reachable vertices with vi and add vi to a stack
While stack is not empty
Get and remove (pop) last vertex v from stack
For all neighbors, vj, of v
If vj is not is set of reachable vertices, add to stack and reachable set
Weighted Graphs Representation: Edge List
Pendleton
Pierre
Pensacola
Princeton
Pittsburgh
Peoria
Pueblo
Phoenix
Pendleton: {Pueblo:8, Phoenix:4}Pensacola: {Phoenix:5}Peoria: {Pueblo:3, Pittsburgh:5}Phoenix: {Pueblo:3, Peoria:4, Pittsburgh:10}Pierre: {Pendleton:2}Pittsburgh: {Pensacola:4}Princeton: {Pittsburgh:2}Pueblo: {Pierre:3}
Instead of a Map of Sets, use a Map of Maps
First key is source, second key is destination, value is
weight
2
8
4
33
3
4
5
10
5
4
2
What about Weighted Graphs?
Pendleton
Pierre
Pensacola
Princeton
Pittsburgh
Peoria
Pueblo
Phoenix
Dijkstra’s algorithm.Use a priority queue instead of a stack. Return a map of city, distance pairs. Pqueue orders values on shortest distance
2
4
3
3
4
3
5
410
8
5
2
Dijkstra (String startCity, Map[String, Map[String, double]] distances)Make empty map of distances into variable reachablePut (StartingCity, 0) into PqueueWhile Pqueue not empty pull new city from queue, if not ready in reachable, add to reachable add neighbors to queue, adding weight to distance from starting cityWhen done with loop, return reachable map
Example: What is the distance from Pierre
Pendleton
Pierre
Pensacola
Princeton
Pittsburgh
Peoria
Pueblo
Phoenix
------------Pierre: 0
2
4
3
3
4
3
5
410
8
5
2
Dijkstra (String startCity, Map[String, Map[String, double]] distances)Make empty map of distances into variable reachablePut (StartingCity, 0) into PqueueWhile Pqueue not empty pull new city from queue, if not ready in reachable, add to reachable add neighbors to queue, adding weight to distance from starting cityWhen done with loop, return reachable map
Example: What is the distance from Pierre
Pendleton
Pierre
Pensacola
Princeton
Pittsburgh
Peoria
Pueblo
Phoenix
Pierre: 0------------ Pendeleton: 2
2
4
3
3
4
3
5
410
8
5
2
Dijkstra (String startCity, Map[String, Map[String, double]] distances)Make empty map of distances into variable reachablePut (StartingCity, 0) into PqueueWhile Pqueue not empty pull new city from queue, if not ready in reachable, add to reachable add neighbors to queue, adding weight to distance from starting cityWhen done with loop, return reachable map
Example: What is the distance from Pierre
Pendleton
Pierre
Pensacola
Princeton
Pittsburgh
Peoria
Pueblo
Phoenix
Pierre: 0, Pendleton: 2------------Phoenix: 6, Pueblo: 10
Notice how the distances have been added
2
4
3
3
4
3
5
410
8
5
2
Dijkstra (String startCity, Map[String, Map[String, double]] distances)Make empty map of distances into variable reachablePut (StartingCity, 0) into PqueueWhile Pqueue not empty pull new city from queue, if not ready in reachable, add to reachable add neighbors to queue, adding weight to distance from starting cityWhen done with loop, return reachable map
Example: What is the distance from Pierre
Pendleton
Pierre
Pensacola
Princeton
Pittsburgh
Peoria
Pueblo
Phoenix
Pierre: 0, Pendleton: 2, Phoenix: 6------------Pueblo: 9, Peoria: 10, Pueblo: 10, Pittsburgh: 16
Notice how values are stored in the Pqueue in distance order
2
4
3
3
4
3
5
410
8
5
2
Dijkstra (String startCity, Map[String, Map[String, double]] distances)Make empty map of distances into variable reachablePut (StartingCity, 0) into PqueueWhile Pqueue not empty pull new city from queue, if not ready in reachable, add to reachable add neighbors to queue, adding weight to distance from starting cityWhen done with loop, return reachable map
Example: What is the distance from Pierre
Pendleton
Pierre
Pensacola
Princeton
Pittsburgh
Peoria
Pueblo
Phoenix
Pierre: 0, Pendleton: 2, Phoenix: 6, Pueblo: 9------------Peoria: 10, Pueblo: 10, Pierre: 13, Pittsburgh: 16
Pierre gets put in queue, although it is known to be reachable
2
4
3
3
4
3
5
410
8
5
2
Dijkstra (String startCity, Map[String, Map[String, double]] distances)Make empty map of distances into variable reachablePut (StartingCity, 0) into PqueueWhile Pqueue not empty pull new city from queue, if not ready in reachable, add to reachable add neighbors to queue, adding weight to distance from starting cityWhen done with loop, return reachable map
Example: What is the distance from Pierre
Pendleton
Pierre
Pensacola
Princeton
Pittsburgh
Peoria
Pueblo
Phoenix
Pierre: 0, Pendleton: 2, Phoenix: 6, Pueblo: 9, Peoria: 10------------Pueblo: 10, Pierre: 13, Pueblo: 13, Pittsburgh: 15, Pittsburgh: 16Duplicates only removed when pulled out of queue
2
4
3
3
4
3
5
410
8
5
2
Dijkstra (String startCity, Map[String, Map[String, double]] distances)Make empty map of distances into variable reachablePut (StartingCity, 0) into PqueueWhile Pqueue not empty pull new city from queue, if not ready in reachable, add to reachable add neighbors to queue, adding weight to distance from starting cityWhen done with loop, return reachable map
Your Turn• Do Dijkstra algorithm starting from Pensacola