1 of 81

Lecture 14: Graph Traversals

CSE 373: Data Structures and Algorithms

CSE 373 22 SP – CHAMPION

1

2 of 81

Administrivia

P3 due August 3rd 11:59PM

  • heaps
  • 2 week assignment
  • should take you 1 week to complete (START EARLY THOOO)

EC 2 Survey due Tomorrow at 11:59P

Final Exam Date

  • take home
  • Due Saturday 20th at Noon NOT Midnight
  • No Late Exceptions
    • like fr fr…..
    • we start grading the second submissions close (literally, we start grading at noon)

3 of 81

Warm Up

  • How would you transform the following scenario into a graph?

  • You are creating a graph representing a brand-new social media network. Each profile has both the option to “friend” another user or to “follow” another user. When ”friend” is selected the other profile is asked for permission, and if given the two profiles will link to one another. If “follow” is selected then no permission is asked, but the recipient will not connect to the follower. Answer the following questions about the graph design:
  • What are the vertices?
  • What are the edges?
  • Undirected or directed?
  • Weighted or unweighted?

Profiles

Follows and friendships

Directed

Unweighted

https://tinyurl.com/Summer373L14

4 of 81

Introduction to Graphs

CSE 373 SP 18 - KASEY CHAMPION

4

5 of 81

Graph: Formal Definition

  • A graph is defined by a pair of sets G = (V, E) where…

CSE 373 SP 18 - KASEY CHAMPION

5

A

B

C

D

E

F

G

H

V = { A, B, C, D, E, F, G, H }

E = { (A, B), (A, C), (A, D), (A, H),

(C, B), (B, D), (D, E), (D, F),

(F, G), (G, H)}

    • V is a set of vertices
      • A vertex or “node” is a data entity
    • E is a set of edges
      • An edge is a connection between two vertices

6 of 81

Graph Glossary

  • Graph: a category of data structures consisting of a set of vertices and a set of edges (pairs of vertices)
    • Labels: additional data on vertices, edges, or both
      • Weighted: a graph where edges have numeric labels
    • Directed: the order of edge pairs matters (edges are arrows) [otherwise undirected]
      • Origin is first in pair, Destination is second in pair
      • In-neighbors of vertex are vertices that point to it, out-neighbors are vertices it points to
      • In-degree: number of edges pointing to vertex, out-degree: number of edges from vertex
    • Cyclic: contains at least one cycle [otherwise acyclic]
    • Simple graph: No self-loops or parallel edges
  • Path: sequence of vertices reachable by edges
    • Simple path: no repeated vertices
    • Cycle: a path that starts and ends at the same vertex
  • Self-loop: edge from vertex to itself
  • Parallel edges: two edges between same vertices in directed graph, going opposite directions

( , )

a

b

c

V: Set of vertices

E: Set of edges

a

b

( , )

a

c

( , )

c

d

a

b

c

e

d

7 of 81

Adjacency Matrix - Runtime

CSE 373 SU 19 – ROBBIE WEBBER

0

1

2

3

4

5

6

0

0

1

1

0

0

0

0

1

1

0

0

1

0

0

0

2

1

0

0

1

0

0

0

3

0

1

1

0

0

1

0

4

0

0

0

0

0

1

0

5

0

0

0

1

1

0

0

6

0

0

0

0

0

0

0

6

2

3

4

5

0

1

In an adjacency matrix a[u][v] is 1 if there is an edge (u,v), and 0 otherwise.

Worst-case Time Complexity �(|V| = n, |E| = m):

Add Edge:

Remove Edge:

Check edge exists from (u,v):

Get outneighbors of u:

Get inneighbors of u:

Space Complexity:

𝚯(1)

𝚯(1)

𝚯(1)

𝚯(n)

𝚯(n)

𝚯(n * n)

8 of 81

Adjacency List

CSE 373 SP 20 - KASEY CHAMPION

8

A

B

C

D

 

 

 

 

 

 

Linked Lists

0

1

2

3

A

B

C

D

A

B

C

B

D

In an adjacency matrix a[u][v] is 1 if there is an edge (u,v), and 0 otherwise.

Worst-case Time Complexity �(|V| = n, |E| = m):

Add Edge:

Remove Edge:

Check edge exists from (u,v):

Get outneighbors of u:

Get inneighbors of u:

Space Complexity:

𝚯(1)

𝚯(deg(u) )

𝚯(deg (u) )

𝚯(deg(u) )

𝚯(n + m)

𝚯(n + m)

9 of 81

Adjacency List

CSE 373 SP 20 - KASEY CHAMPION

9

0

1

2

3

4

0

1

2

3

4

0

1

2

3

4

A

B

C

D

Hash Tables

0

1

2

3

A

B

C

D

C

D

A

B

B

 

 

 

 

 

 

In an adjacency matrix a[u][v] is 1 if there is an edge (u,v), and 0 otherwise.

Worst-case Time Complexity �(|V| = n, |E| = m):

Add Edge:

Remove Edge:

Check edge exists from (u,v):

Get outneighbors of u:

Get inneighbors of u:

Space Complexity:

𝚯(1)

𝚯(1)

𝚯(1)

𝚯(deg(u) )

𝚯(n)

𝚯(n + m)

10 of 81

Adapting for Undirected Graphs

A

B

C

D

E

A

0

1

1

0

0

B

1

0

1

1

0

C

1

1

0

1

0

D

0

1

1

0

0

E

0

0

0

0

0

destination

origin

Abstraction of the Hash Map! Buckets not shown.

A

C

B

Keys (origins)

Values (hashmaps w/ destinations as keys)

B

C

1

1

B

D

1

1

B

1

A

B

C

E

D

Adjacency Matrix

Store each edge as both directions

(makes the matrix symmetrical)

Adjacency List

Store each edge as both directions

(doubles the number of entries)

A

1

C

1

A

C

1

1

D

1

D

11 of 81

Tradeoffs

  •  

12 of 81

373: Assumed Graph Implementations

  • For this class, unless otherwise stated, assume we’re using an adjacency list with hash maps.
    • Also unless otherwise stated, assume all graph hash map operations are O(1). This is a pretty reasonable assumption, because for most problems we examine you know the set of vertices ahead of time and can prevent resizing.

Add Edge

Remove Edge

Check if edge (u, v) exists

Get out-neighbors of u

Get in-neighbors of v

(Space Complexity)

 

 

 

 

 

 

(|V| = n, |E| = m)

13 of 81

Graph Traversals

CSE 373 SP 18 - KASEY CHAMPION

13

14 of 81

s-t Connectivity Problem

  • Try to come up with an algorithm for connected(s, t)

s-t Connectivity Problem

Given source vertex s and a target vertex t, does there exist a path between s and t?

1

2

3

4

5

6

7

8

0

s

t

    • We can use recursion: if a neighbor of s is connected to t, that means s is also connected to t!

15 of 81

s-t Connectivity Problem: Proposed Solution

connected(Vertex s, Vertex t) {

if (s == t) {

return true;

} else {

for (Vertex n : s.neighbors) {

if (connected(n, t)) {

return true;

}

}

return false;

}

}

1

2

3

4

5

6

7

8

0

s

t

16 of 81

What’s wrong with this proposal?

Does 0 == 7? No; if(connected(1, 7) return true;

Does 1 == 7? No; if(connected(0, 7) return true;

Does 0 == 7?

connected(Vertex s, Vertex t) {

if (s == t) {

return true;

} else {

for (Vertex n : s.neighbors) {

if (connected(n, t)) {

return true;

}

}

return false;

}

}

1

2

3

4

5

6

7

8

0

s

t

17 of 81

s-t Connectivity Problem: Better Solution

  • Solution: Mark each node as visited!

Set<Vertex> visited; // assume global

connected(Vertex s, Vertex t) {

if (s == t) {

return true;

} else {

visited.add(s);

for (Vertex n : s.neighbors) {

if (!visited.contains(n)) {

if (connected(n, t)) {

return true;

}

}

}

return false;

}

}

1

2

3

4

5

6

7

8

0

s

t

This general approach for crawling through a graph is going to be the basis for a LOT of algorithms!

18 of 81

Set<Vertex> visited; // assume global

connected(Vertex s, Vertex t) {

if (s == t) {

return true;

} else {

visited.add(s);

for (Vertex n : s.neighbors) {

if (!visited.contains(n)) {

if (connected(n, t)) {

return true;

}

}

}

return false;

}

}

Recursive Depth-First Search (DFS)

  • What order does this algorithm use to visit nodes?
    • Assume order of s.neighbors is arbitrary!
  • It will explore one option “all the way down” before coming back to try other options
    • Many possible orderings: {0, 1, 2, 5, 6, 9, 7, 8, 4, 3} or �{0, 1, 4, 3, 2, 5, 8, 6, 7, 9} both possible

1

2

3

4

5

6

7

8

s

VISITED

9

  • We call this approach a depth-first search (DFS)

19 of 81

  • We could also apply this code to a tree (recall: a type of graph) to do a depth-first search on it

Set<Vertex> visited; // assume global

connected(Vertex s, Vertex t) {

if (s == t) {

return true;

} else {

visited.add(s);

for (Vertex n : s.neighbors) {

if (!visited.contains(n)) {

if (connected(n, t)) {

return true;

}

}

}

return false;

}

}

1

2

3

4

5

8

0

s

  • CSE 143 Review traversing a binary tree depth-first has 3 options:
    • Pre-order: visit node before its children
    • In-order: visit node between its children
    • Post-order: visit node after its children

VISITED

Aside Tree Traversals

The difference between these orderings is when we “process” the root – all are DFS!

20 of 81

Breadth-First Search (BFS)

  • Suppose we want to visit closer nodes first, instead of following one choice all the way to the end
    • Just like level-order traversal of a tree, now generalized to any graph

1

2

3

4

5

6

7

8

s

VISITED

9

We call this approach a breadth-first search (BFS)

for (Vertex n : s.neighbors) {

0

1

2

3

4

This is our goal, but how do we translate into code?

    • Key observation: recursive calls interrupted s.neighbors loop to immediately process children
    • For BFS, instead we want to complete that loop before processing children
    • Recursion isn’t the answer! Need a data structure to ”queue up” children…
    • Explore “layer by layer”

21 of 81

BFS Implementation

bfs(Graph graph, Vertex start) {

Our extra data structure! Will keep track of “outer edge” of nodes still to explore

Let’s make this a bit more realistic and add a Graph

Kick off the algorithm by adding start to perimeter

1

2

3

4

5

6

7

8

9

start

Grab one element at a time from the perimeter

Look at all that element’s children

Add new ones to perimeter!

Queue<Vertex> perimeter = new Queue<>();

Set<Vertex> visited = new Set<>();

perimeter.add(start);

visited.add(start);

while (!perimeter.isEmpty()) {

Vertex from = perimeter.remove();

for (Edge edge : graph.edgesFrom(from)) {

Vertex to = edge.to();

if (!visited.contains(to)) {

perimeter.add(to);

visited.add(to);

}

}

}

}

22 of 81

BFS Implementation: In Action

PERIMETER

bfs(Graph graph, Vertex start) {

Queue<Vertex> perimeter = new Queue<>();

Set<Vertex> visited = new Set<>();

perimeter.add(start);

visited.add(start);

while (!perimeter.isEmpty()) {

Vertex from = perimeter.remove();

for (Edge edge : graph.edgesFrom(from)) {

Vertex to = edge.to();

if (!visited.contains(to)) {

perimeter.add(to);

visited.add(to);

}

}

}

}

1

2

3

4

5

6

7

8

start

VISITED

9

1

2

4

5

3

6

8

9

7

23 of 81

BFS Intuition: Why Does it Work?

0

1

2

3

4

PERIMETER

bfs(Graph graph, Vertex start) {

Queue<Vertex> perimeter = new Queue<>();

Set<Vertex> visited = new Set<>();

perimeter.add(start);

visited.add(start);

while (!perimeter.isEmpty()) {

Vertex from = perimeter.remove();

for (Edge edge : graph.edgesFrom(from)) {

Vertex to = edge.to();

if (!visited.contains(to)) {

perimeter.add(to);

visited.add(to);

}

}

}

}

1

2

3

4

5

6

7

8

start

VISITED

9

1

2

4

5

3

6

8

9

7

  • Properties of a queue exactly what gives us this incredibly cool behavior
  • As long as we explore an entire layer before moving on (and we will, with a queue) the next layer will be fully built up and waiting for us by the time we finish!
  • Keep going until perimeter is empty

24 of 81

BFS’s Evil Twin: DFS!

bfs(Graph graph, Vertex start) {

Queue<Vertex> perimeter = new Queue<>();

Set<Vertex> visited = new Set<>();

perimeter.add(start);

visited.add(start);

while (!perimeter.isEmpty()) {

Vertex from = perimeter.remove();

for (Edge edge : graph.edgesFrom(from)) {

Vertex to = edge.to();

if (!visited.contains(to)) {

perimeter.add(to);

visited.add(to);

}

}

}

}

dfs(Graph graph, Vertex start) {

Stack<Vertex> perimeter = new Stack<>();

Set<Vertex> visited = new Set<>();

perimeter.add(start);

while (!perimeter.isEmpty()) {

Vertex from = perimeter.remove();

if (!visted.contains(from)) {

for (Edge edge:graph.edgesFrom(from)) {

Vertex to = edge.to();

perimeter.add(to)

}

visited.add(from);

}

}

}

In DFS we can’t immediately add a node as “visited”. We need to make sure we are only marking the node when it is popped.

Change Queue for order to process neighbors to a Stack

25 of 81

Recap: Graph Traversals

  • We’ve seen two approaches for ordering a graph traversal
  • BFS and DFS are just techniques for iterating! (think: for loop over an array)
    • Need to add code that actually processes something to solve a problem
    • A lot of interview problems on graphs can be solved with modifications on top of BFS or DFS! Very worth being comfortable with the pseudocode ☺

BFS

(Iterative)

  • Explore layer-by-layer: examine every node at a certain distance from start, then examine nodes that are one level farther
  • Uses a queue!

DFS

(Iterative)

  • Follow a “choice” all the way to the end, then come back to revisit other choices
  • Uses a stack!

DFS

(Recursive)

Be careful using this – on huge graphs, might overflow the call stack

Let’s Practice Now!

26 of 81

Using BFS for the s-t Connectivity Problem

  • BFS is a great building block – all we have to do is check each node to see if we’ve reached t!
    • Note: we’re not using any specific properties of BFS here, we just needed a traversal. DFS would also work.

s-t Connectivity Problem

Given source vertex s and a target vertex t, does there exist a path between s and t?

stCon(Graph graph, Vertex start, Vertex t) {

Queue<Vertex> perimeter = new Queue<>();

Set<Vertex> visited = new Set<>();

perimeter.add(start);

visited.add(start);

while (!perimeter.isEmpty()) {

Vertex from = perimeter.remove();

if (from == t) { return true; }

for (Edge edge : graph.edgesFrom(from)) {

Vertex to = edge.to();

if (!visited.contains(to)) {

perimeter.add(to);

visited.add(to);

}

}

}

return false;

}

27 of 81

Topological Sort

28 of 81

Topological Sort

  • A topological sort of a directed graph G is�an ordering of the nodes, where for every edge in the graph, the origin appears before the destination in the ordering

  • Intuition: a “dependency graph”
    • An edge (u, v) means u must happen before v
    • A topological sort of a dependency graph gives an ordering that respects dependencies�
  • Applications:
    • Graduating
    • Compiling multiple Java files
    • Multi-job Workflows

A

B

C

A before C

B before C

A before B

A

B

C

Topological Sort:

With original edges for reference:

A

B

C

Input:

29 of 81

Problem 1: Ordering Dependencies

  • Today’s (first) problem: Given a bunch of courses with prerequisites, find an order to take the courses in.

CSE 373 SP 18 - KASEY CHAMPION

29

Math 126

CSE 142

CSE 143

CSE 373

CSE 374

CSE 417

30 of 81

Ordering a DAG

  • Does this graph have a topological ordering? If so find one.

CSE 373 19 WI - KASEY CHAMPION

30

A

B

C

E

D

If a vertex doesn’t have any edges going into it, we can add it to the ordering.

More generally, if the only incoming edges are from vertices already in the ordering, it’s safe to add.

0

1

2

1

1

A

C

B

D

E

0

1

0

0

0

31 of 81

Problem 1: Ordering Dependencies

  • Given a directed graph G, where we have an edge from u to v if u must happen before v.
  • We can only do things one at a time, can we find an order that respects dependencies?

CSE 373 19 SP - KASEY CHAMPION

31

Given: a directed graph G

Find: an ordering of the vertices so all edges go from left to right (all the dependency arrows are satisfied and the vertices can be processed left to right with no problems) .

Topological Sort (aka Topological Ordering)

32 of 81

Topological Ordering

  • A course prerequisite chart and a possible topological ordering.

CSE 373 19 SP - KASEY CHAMPION

32

Math 126

CSE 142

CSE 143

CSE 373

CSE 374

CSE 417

Math 126

CSE 142

CSE 143

CSE 373

CSE 374

CSE 417

33 of 81

Can We Always Topo Sort a Graph?

  • Can you topologically sort this graph?�����
  • What’s the difference between this graph and our first graph?����
  • A graph has a topological ordering if it is a DAG
    • But a DAG can have multiple orderings

CSE 143

CSE 373

CSE 417

🤔 Where do I start?

Where do I end? 🤔

MATH 126

CSE 142

CSE 143

CSE 373

CSE 374

CSE 417

No 😭

DIRECTED ACYCLIC GRAPH

  • A directed graph without any cycles
  • Edges may or may not be weighted

34 of 81

Topological Sort Pseudocode

CSE 373 SP 22 - KASEY CHAMPION

toposort(Graph graph) {

Queue<Vertex> perimeter = new Queue<>();

Set<Vertex> visited = new Set<>();

Map<Vertex, Integer> indegree = countInDegree(graph);

for (Vertex v : indegree.keySet()) {

if(indegree.get(v) == 0) {

perimeter.add(v);

visited.add(v);

}

}

while (!perimeter.isEmpty()) {

Vertex from = perimeter.remove();

for (Edge edge : graph.edgesFrom(from)) {

Vertex to = edge.to();

if (!visited.contains(to)) {

int inDeg = indegree.get(to);

inDeg--;

if (inDeg == 0) {

perimeter.add(to);

visited.add(to);

}

indegree.put(to, inDeg);

}...

Start with BFS code (Queue to visit neighbors, List to mark visited)

Count the in-degree of each vertex

queue up the 0 in-degree nodes to visit

Loop over Queue

for each neighbor of a visited node reduce their in-degree count

for nodes that hit 0, add them to Queue

Toposort is order nodes are “visited” (could create separate List to track order, could print out as you add to Set)

35 of 81

Shortest Path

CSE 373 SP 22 - KASEY CHAMPION

35

36 of 81

The Shortest Path Problem

  • This is a little harder, but still totally doable! We just need a way to keep track of how far each node is from the start.
    • Sounds like a job for?

(Unweighted) Shortest Path Problem

Given source vertex s and a target vertex t, how long is the shortest path from s to t? What edges make up that path?

37 of 81

Using BFS for the Shortest Path Problem

  • This is a little harder, but still totally doable! We just need a way to keep track of how far each node is from the start.
    • Sounds like a job for?
      • BFS!

(Unweighted) Shortest Path Problem

Given source vertex s and a target vertex t, how long is the shortest path from s to t? What edges make up that path?

...

Map<Vertex, Edge> edgeTo = ...

Map<Vertex, Double> distTo = ...

edgeTo.put(start, null);

distTo.put(start, 0.0);

while (!perimeter.isEmpty()) {

Vertex from = perimeter.remove();

for (Edge edge : graph.edgesFrom(from)) {

Vertex to = edge.to();

if (!visited.contains(to)) {

edgeTo.put(to, edge);

distTo.put(to, distTo.get(from) + 1);

perimeter.add(to);

visited.add(to);

}

}

}

return edgeTo;

}

Remember how we got to this point, and what layer this vertex is part of

The start required no edge to arrive at, and is on level 0

38 of 81

BFS for Shortest Paths: Example

  • The edgeTo map stores backpointers: each vertex remembers what vertex was used to arrive at it!
  • Note: this code stores visited, edgeTo, and distTo as external maps (only drawn on graph for convenience). Another implementation option: store them as fields of the nodes themselves

A

B

E

C

D

start

VISITED

PERIMETER

...

Map<Vertex, Edge> edgeTo = ...

Map<Vertex, Double> distTo = ...

edgeTo.put(start, null);

distTo.put(start, 0.0);

while (!perimeter.isEmpty()) {

Vertex from = perimeter.remove();

for (Edge edge : graph.edgesFrom(from)) {

Vertex to = edge.to();

if (!visited.contains(to)) {

edgeTo.put(to, edge);

distTo.put(to, distTo.get(from) + 1);

perimeter.add(to);

visited.add(to);

}

}

}

return edgeTo;

}

EDGETO

DISTTO

0

1

1

2

2

A

B

C

D

E

39 of 81

What about the Target Vertex?

  • This modification on BFS didn’t mention the target vertex at all!
  • Instead, it calculated the shortest path and distance from start to every other vertex
    • This is called the shortest path tree
      • A general concept: in this implementation, made up of distances and backpointers
  • Shortest path tree has all the answers!
    • Length of shortest path from A to D?
      • Lookup in distTo map: 2
    • What’s the shortest path from A to D?
      • Build up backwards from edgeTo map: start at D, follow backpointer to B, follow backpointer to A – our shortest path is A 🡪 B 🡪 D

  • All our shortest path algorithms will have this property
    • If you only care about t, you can sometimes stop early!

A

B

E

C

D

start

EDGETO

DISTTO

0

1

1

2

2

Shortest Path Tree:

40 of 81

Recap: Graph Problems

  • Just like everything is Graphs, every problem is a Graph Problem
  • BFS and DFS are very useful tools to solve these! We’ll see plenty more.

EASY

MEDIUM

s-t Connectivity Problem

Given source vertex s and a target vertex t, does there exist a path between s and t?

(Unweighted) Shortest Path Problem

Given source vertex s and a target vertex t, how long is the shortest path from s to t? What edges make up that path?

BFS or DFS + check if we’ve hit t

BFS + generate shortest path tree as we go

What about the Shortest Path Problem on a weighted graph?

41 of 81

Next Stop Weighted Shortest Paths

HARDER (FOR NOW)

  • Suppose we want to find shortest path from A to C, using weight of each edge as “distance”
  • Is BFS going to give us the right result here?

A

B

C

D

14.0

12.0

9000.2

1.5

start

target

42 of 81

Dijkstra’s Algorithm

  • Named after its inventor, Edsger Dijkstra (1930-2002)
    • Truly one of the “founders” of computer science
    • 1972 Turing Award
    • This algorithm is just one of his many contributions!
    • Example quote: “Computer science is no more about computers than astronomy is about telescopes”

  • The idea: reminiscent of BFS, but adapted to handle weights
    • Grow the set of nodes whose shortest distance has been computed
    • Nodes not in the set will have a “best distance so far”

43 of 81

Dijkstra’s Algorithm: Idea

  • Initialization:
    • Start vertex has distance 0; all other vertices have distance

  • At each step:
    • Pick closest unknown vertex v
    • Add it to the “cloud” of known vertices
    • Update “best-so-far” distances for vertices with edges from v

A

B

D

C

F

H

E

G

2

2

3

1

10

2

3

1

11

7

1

9

2

4

5

0

4

2

1

4??

12??

KNOWN

UNKNOWN

PERIMETER

start

44 of 81

dijkstraShortestPath(G graph, V start)

Dijkstra’s Pseudocode (High-Level)

Similar to “visited” in BFS, “known” is nodes that are finalized (we know their path)

Dijkstra’s algorithm is all about updating “best-so-far” in distTo if we find shorter path! Init all paths to infinite.

Order matters: always visit closest first!

Consider all vertices reachable from me: would getting there through me be a shorter path than they currently know about?

  • Suppose we already visited B, distTo[D] = 7
  • Now considering edge (C, D):
    • oldDist = 7
    • newDist = 3 + 1
    • That’s better! Update distTo[D], edgeTo[D]

C

D

B

A

KNOWN

PERIMETER

0

2

3

7??

2

3

5

1

start

u

v

Set known; Map edgeTo, distTo;

initialize distTo with all nodes mapped to ∞, except start to 0

while (there are unknown vertices):

let u be the closest unknown vertex

known.add(u);

for each edge (u,v) from u with weight w:

oldDist = distTo.get(v) // previous best path to v

newDist = distTo.get(u) + w // what if we went through u?

if (newDist < oldDist):

distTo.put(v, newDist)

edgeTo.put(v, u)

45 of 81

Dijkstra’s Algorithm: Key Properties

  • Once a vertex is marked known, its shortest path is known
    • Can reconstruct path by following back-pointers (in edgeTo map)

dijkstraShortestPath(G graph, V start)

Set known; Map edgeTo, distTo;

initialize distTo with all nodes mapped to ∞, except start to 0

while (there are unknown vertices):

let u be the closest unknown vertex

known.add(u)

for each edge (u,v) to unknown v with weight w:

oldDist = distTo.get(v) // previous best path to v

newDist = distTo.get(u) + w // what if we went through u?

if (newDist < oldDist):

distTo.put(v, newDist)

edgeTo.put(v, u)�

  • While a vertex is not known, another shorter path might be found
    • We call this update relaxing the distance because it only ever shortens the current best path�
  • Going through closest vertices first lets us confidently say no shorter path will be found once known
    • Because not possible to find a shorter path that uses a farther vertex we’ll consider later

46 of 81

Dijkstra’s Algorithm: Example #1

46

Order Added to Known Set:

Vertex

Known?

distTo

edgeTo

A

B

C

D

E

F

G

H

A

B

D

C

F

H

E

G

2

2

3

1

10

2

3

1

11

7

1

9

2

4

5

0

start

47 of 81

Dijkstra’s Algorithm: Example #1

47

Order Added to Known Set:

A

Vertex

Known?

distTo

edgeTo

A

Y

0

/

B

≤ 2

A

C

≤ 1

A

D

≤ 4

A

E

F

G

H

A

B

D

C

F

H

E

G

2

2

3

1

10

2

3

1

11

7

1

9

2

4

5

2??

1??

4??

0

start

48 of 81

Dijkstra’s Algorithm: Example #1

48

Order Added to Known Set:

A, C

Vertex

Known?

distTo

edgeTo

A

Y

0

/

B

≤ 2

A

C

Y

1

A

D

≤ 4

A

E

≤ 12

C

F

G

H

A

B

D

C

F

H

E

G

2

2

3

1

10

2

3

1

11

7

1

9

2

4

5

2??

1

4??

12??

0

start

49 of 81

Dijkstra’s Algorithm: Example #1

49

Order Added to Known Set:

A, C, B

Vertex

Known?

distTo

edgeTo

A

Y

0

/

B

Y

2

A

C

Y

1

A

D

≤ 4

A

E

≤ 12

C

F

≤ 4

B

G

H

A

B

D

C

F

H

E

G

2

2

3

1

10

2

3

1

11

7

1

9

2

4

5

2

4??

1

4??

12??

0

start

50 of 81

Dijkstra’s Algorithm: Example #1

50

Order Added to Known Set:

A, C, B, D

Vertex

Known?

distTo

edgeTo

A

Y

0

/

B

Y

2

A

C

Y

1

A

D

Y

4

A

E

≤ 12

C

F

≤ 4

B

G

H

A

B

D

C

F

H

E

G

2

2

3

1

10

2

3

1

11

7

1

9

2

4

5

2

4??

1

4

12??

0

start

51 of 81

Dijkstra’s Algorithm: Example #1

51

Order Added to Known Set:

A, C, B, D, F

Vertex

Known?

distTo

edgeTo

A

Y

0

/

B

Y

2

A

C

Y

1

A

D

Y

4

A

E

≤ 12

C

F

Y

4

B

G

H

≤ 7

F

A

B

D

C

F

H

E

G

2

2

3

1

10

2

3

1

11

7

1

9

2

4

5

2

4

7??

1

4

12??

0

start

52 of 81

Dijkstra’s Algorithm: Example #1

52

Order Added to Known Set:

A, C, B, D, F, H

Vertex

Known?

distTo

edgeTo

A

Y

0

/

B

Y

2

A

C

Y

1

A

D

Y

4

A

E

≤ 12

C

F

Y

4

B

G

≤ 8

H

H

Y

7

F

A

B

D

C

F

H

E

G

2

2

3

1

10

2

3

1

11

7

1

9

2

4

5

2

4

7

1

4

8??

12??

0

start

53 of 81

Dijkstra’s Algorithm: Example #1

53

Order Added to Known Set:

A, C, B, D, F, H, G

Vertex

Known?

distTo

edgeTo

A

Y

0

/

B

Y

2

A

C

Y

1

A

D

Y

4

A

E

≤ 11

G

F

Y

4

B

G

Y

8

H

H

Y

7

F

A

B

D

C

F

H

E

G

2

2

3

1

10

2

3

1

11

7

1

9

2

4

5

2

4

7

1

4

8

11??

0

start

54 of 81

Dijkstra’s Algorithm: Example #1

54

Order Added to Known Set:

A, C, B, D, F, H, G, E

Vertex

Known?

distTo

edgeTo

A

Y

0

/

B

Y

2

A

C

Y

1

A

D

Y

4

A

E

Y

11

G

F

Y

4

B

G

Y

8

H

H

Y

7

F

A

B

D

C

F

H

E

G

2

2

3

1

10

2

3

1

11

7

1

9

2

4

5

2

4

7

1

4

8

11

0

start

55 of 81

Dijkstra’s Algorithm: Interpreting the Results

Now that we’re done, how do we get the path from A to E?

  • Follow edgeTo backpointers!
  • distTo and edgeTo make up the shortest path tree

55

Order Added to Known Set:

A, C, B, D, F, H, G, E

Vertex

Known?

distTo

edgeTo

A

Y

0

/

B

Y

2

A

C

Y

1

A

D

Y

4

A

E

Y

11

G

F

Y

4

B

G

Y

8

H

H

Y

7

F

A

B

D

C

F

H

E

G

2

2

3

1

10

2

3

1

11

7

1

9

2

4

5

2

4

7

1

4

8

11

0

start

56 of 81

Review: Key Features

  • Once a vertex is marked known, its shortest path is known
    • Can reconstruct path by following backpointers
  • While a vertex is not known, another shorter path might be found!

  • The “Order Added to Known Set” is unimportant
    • A detail about how the algorithm works (client doesn’t care)
    • Not used by the algorithm (implementation doesn’t care)
    • It is sorted by path-distance; ties are resolved “somehow”

  • If we only need path to a specific vertex, can stop early once that vertex is known
    • Because its shortest path cannot change!
    • Return a partial shortest path tree

57 of 81

Appendix

58 of 81

Graph problems

  • There are lots of interesting questions we can ask about a graph:
  • ▪ What is the shortest route from S to T?

▪ What is the longest without cycles?

  • ▪ Are there cycles?
  • ▪ Is there a tour (cycle) you can take that only uses each node (station) exactly once?
  • ▪ Is there a tour (cycle) that uses each edge exactly once?

HANNAH TANG 20WI

59 of 81

Graph problems

  • Some well known graph problems and their common names:
  • ▪ s-t Path. Is there a path between vertices s and t?
  • ▪ Connectivity. Is the graph connected?
  • ▪ Biconnectivity. Is there a vertex whose removal disconnects the graph?
  • ▪ Shortest s-t Path. What is the shortest path between vertices s and t?
  • ▪ Cycle Detection. Does the graph contain any cycles?
  • ▪ Euler Tour. Is there a cycle that uses every edge exactly once?
  • ▪ Hamilton Tour. Is there a cycle that uses every vertex exactly once?
  • ▪ Planarity. Can you draw the graph on paper with no crossing edges?
  • ▪ Isomorphism. Are two graphs the same graph (in disguise)?
  • Graph problems are among the most mathematically rich areas of CS theory!

HANNAH TANG 20WI

60 of 81

s-t path Problem

  • s-t path problem
    • Given source vertex s and a target vertex t, does there exist a path between s and t?

  • Why does this problem matter? Some possible context:
      • real life maps and trip planning – can we get from one location (vertex) to another location (vertex) given the current available roads (edges)
      • family trees and checking ancestry – are two people (vertices) related by some common ancestor (edges for direct parent/child relationships)
      • game states (Artificial Intelligence) can you win the game from the current vertex (think: current board position)? Are there moves (edges) you can take to get to the vertex that represents an already won game?

60

1

2

3

4

5

6

7

8

0

s

t

61 of 81

s-t path Problem

  • s-t path problem
    • Given source vertex s and a target vertex t, does there exist a path between s and t?

61

1

2

3

4

5

6

7

8

0

s

t

  • What’s the answer for this graph on the left, and how did we get that answer as humans?
    • We can see there’s edges that are visually in between s and t, and we can try out an example path and make sure that by traversing that path you can get from s to t.
    • We know that doesn’t scale that well though, so now let’s try to define a more algorithmic (comprehensive) way to find these paths. The main idea is: starting from the specified s, try traversing through every single possible path possible that’s not redundant to see if it could lead to t.

traversals are really important to solving this problem / problems in general, so slight detour to talk about them, we’ll come back to this though

62 of 81

Graph traversals: DFS

  • Depth First Search - a traversal on graphs (or on trees since those are also graphs) where you traverse “deep nodes” before all the shallow ones
  • High-level DFS: you go as far as you can down one path till you hit a dead end (no neighbors are still undiscovered or you have no neighbors). Once you hit a dead end, you backtrack / undo until you find some options/edges that you haven’t actually tried yet.

Kind of like wandering a maze – if you get stuck at a dead end (since you physically have to go and try it out to know it’s a dead end), trace your steps backwards towards your last decision and when you get back there, choose a different option than you did before.

one valid DFS traversal: 10, 5, 3, 2, 4, 8, 7,6, 9, 15, 12, 14, 18

63 of 81

Graph traversals: BFS

  • Breadth First Search - a traversal on graphs (or on trees since those are also graphs) where you traverse level by level. So in this one we’ll get to all the shallow nodes before any “deep nodes”.
  • Intuitive ways to think about BFS:
  • - opposite way of traversing compared to DFS
  • - a sound wave spreading from a starting point, going outwards in all directions possible.
  • - mold on a piece of food spreading outwards so that it eventually covers the whole surface

one valid BFS traversal: 10, 5, 15, 3, 8, 12, 18, 2, 4, 7, 9, 14, 6

64 of 81

Graph traversals: BFS and DFS on more graphs

  • Take 2 minutes and try to come up with two possible traversal orderings starting with the 0 node:
  • a BFS ordering (ordering within each layer doesn’t matter / any ordering is valid)
  • a DFS ordering (ordering which path you choose next at any point doesn’t matter / any is valid as long as you haven’t explored it before)
  • @ordering choices will be more stable when we have code in front of us, but not the focus / point of the traversals so don’t worry about it

In DFS, you go as far as you can down one path till you hit a dead end (no neighbors are still undiscovered or you have no neighbors). Once you hit a dead end, you backtrack / undo until you find some options/edges that you haven’t actually tried yet.

In BFS, you traverse level by level

65 of 81

Graph traversals: BFS and DFS on more graphs

  • Take a minute and try to come up with two possible traversal orderings starting with the 0 node:
  • a BFS ordering (ordering within each layer doesn’t really matter / any ordering is valid)
    • 0, [1, 2, 3, 4, 5, 6, 7], [8, 9, 10, 12, 13, 14, 15, 16, 17], [11, 18], [19]
  • a DFS ordering (ordering which path you choose next at any point doesn’t matter / any is valid as long as you haven’t explored it before)
    • 0, 2, 9, 3, 10, 11, 19, 4, 12, 18, �5, 13, 14, 6, 15, 7, 16, 1,17, 8

In DFS, you go as far as you can down one path till you hit a dead end (no neighbors are still undiscovered or you have no neighbors). Once you hit a dead end, you backtrack / undo until you find some options/edges that you haven’t actually tried yet.

In BFS, you traverse level by level

66 of 81

Graph traversals: BFS and DFS on more graphs

https://visualgo.net/en/dfsbfs

  • click on draw graph to create your own graphs and run BFS/DFS on them!
  • check out visualgo.net for more really cool interactive visualizations
  • or do your own googling – there are a lot of cool visualizations out there ☺!

67 of 81

BFS pseudocode (some details not Java specific)

  • bfs(Graph graph, Vertex start) {
  • // stores the remaining vertices to visit in the BFS
  • Queue<Vertex> perimeter = new Queue<>();
  • // stores the set of discovered vertices so we don't revisit them multiple times
  • Set<Vertex> discovered = new Set<>();
  • // kicking off our starting point by adding it to the perimeter
  • perimeter.add(start);
  • discovered.add(start);

  • while (!perimeter.isEmpty()) {
  • Vertex from = perimeter.remove();
  • for (E edge : graph.outgoingEdgesFrom(from)) {
  • Vertex to = edge.to();
  • if (!discovered.contains(to)) {
  • perimeter.add(to);
  • discovered.add(to)
  • }
  • }
  • }

1

2

3

4

5

6

7

8

0

s

t

68 of 81

BFS pseudocode (some details not Java specific)

  • //. . . this is the main loop/code for BFS
  • while (!perimeter.isEmpty()) {
  • Vertex from = perimeter.remove();
  • for (E edge : graph.outgoingEdgesFrom(from)) {
  • Vertex to = edge.to();
  • if (!discovered.contains(to)) {
  • perimeter.add(to);
  • discovered.add(to)
  • }
  • }
  • }

1

2

3

4

5

6

7

8

0

s

t

Perimeter queue:

Discovered set:

Expected levels starting the BFS from 0:

  • 0
  • 1
  • 2 4
  • 3 5
  • 6 8
  • 7

69 of 81

DFS pseudocode (some details not Java specific)

  • dfs(Graph graph, Vertex start) {
  • // stores the remaining vertices to visit in the DFS
  • Stack<Vertex> perimeter = new Stack<>(); //the only change you need to make to do DFS!
  • // stores the set of discovered vertices so we don't revisit them multiple times
  • Set<Vertex> discovered = new Set<>();
  • // kicking off our starting point by adding it to the perimeter
  • perimeter.add(start);
  • discovered.add(start);

  • while (!perimeter.isEmpty()) {
  • Vertex from = perimeter.remove();
  • for (E edge : graph.outgoingEdgesFrom(from)) {
  • Vertex to = edge.to();
  • if (!discovered.contains(to)) {
  • perimeter.add(to);
  • discovered.add(to)
  • }
  • }
  • }

1

2

3

4

5

6

7

8

0

s

t

70 of 81

Modifying BFS and DFS

BFS and DFS are like the for loops over arrays for graphs. They’re super fundamental to so many ideas, but when they’re by themselves they don’t do anything. Consider the following code:

for (int i = 0; i < n; i++) {

int x = arr[i];

}

We actually need to do something with the data for it to be useful!

A lot of times to solve basic graph problems (which show up in technical interviews at this level), and often the answer is that you just need to describe / implement BFS/DFS with a small modification for your specific problem.

Now back to the s-t path problem…

  • while (!perimeter.isEmpty()) {
  • Vertex from = perimeter.remove();
  • for (E edge : graph.outgoingEdgesFrom(from)) {
  • Vertex to = edge.to();
  • if (!discovered.contains(to)) {
  • perimeter.add(to, newDist);
  • discovered.add(to)
  • }
  • }
  • }

71 of 81

Modifying BFS for the s-t path problem

  • //. . . this is the main loop/code for BFS
  • while (!perimeter.isEmpty()) {
  • Vertex from = perimeter.remove();
  • for (E edge : graph.outgoingEdgesFrom(from)) {
  • Vertex to = edge.to();
  • if (!discovered.contains(to)) {
  • perimeter.add(to);
  • discovered.add(to)
  • }
  • }
  • }
  • // with modifications to return true if
  • // there is a path where s can reach t
  • while (!perimeter.isEmpty()) {
  • Vertex from = perimeter.remove();
  • if (from == t) {
  • return true;
  • }
  • for (E edge : graph.outgoingEdgesFrom(from)) {
  • Vertex to = edge.to();
  • if (!discovered.contains(to)) {
  • perimeter.add(to);
  • discovered.add(to)
  • }
  • }
  • }
  • return false;

72 of 81

  • Small note: for this s-t problem, we didn’t really need the power of BFS in particular, just some way of looping through the graph starting at a particular point and seeing everything it was connected to. So we could have just as easily used DFS.

73 of 81

Questions / clarifications on anything?

we covered:

  • s-t path problem
  • BFS/DFS visually + high-level
  • BFS/DFS pseudocode
  • modifying BFS/DFS to solve s-t path problem

74 of 81

Roadmap for today

  • review Wednesday intro to graphs key points
  • graph problems
  • s-t path problem
  • detour: BFS/DFS
    • visually
    • pseudocode
    • modifications to solve problems (circling back to s-t path)
  • shortest path problem (for unweighted graphs)

75 of 81

Shortest Path problem (unweighted graph)

  • For the graph on the right, find the shortest path (the path that has the fewest number of edges) between the 0 node and the 5 node. Describe the path by describing each edge (i.e. (0, 1) edge).
  • What’s the answer? How did we get that as humans? How do we want to do it comprehensively defined in an algorithm?

1

2

4

5

6

7

8

0

s

t

76 of 81

Shortest Path problem (unweighted graph)

  • how do we find a shortest paths?

  • What’s the shortest path from 0 to 0?
    • Well….we’re already there.
  • What’s the shortest path from 0 to 1 or 8?
    • Just go on the edge from 0
  • From 0 to 4 or 2 or 5?
    • Can’t get there directly from 0, if we want a length 2 path, have to go through 1 or 8.
  • From 0 to 6?
    • Can’t get there directly from 0, if we want a length 3 path, have to go through 5.

CSE 373 19 SU - ROBBIE WEBER

76

1

2

4

5

6

7

8

0

s

t

77 of 81

Shortest Path problem (unweighted graph) key idea

  • To find the set of vertices at distance k, just find the set of vertices at distance k-1, and see if any of them have an outgoing edge to an undiscovered vertex. Basically, if we traverse level by level and we’re checking all the nodes that show up at each level comprehensively (and only recording the earliest time they show up), when we find our target at level k, we can keep using the edge that led to it from the previous level to justify the shortest path.
  • Do we already know an algorithm that can help us traverse the graph level by level?
  • Yes! BFS! Let’s modify it to fit our needs.

CSE 373 19 SU - ROBBIE WEBER

77

perimeter.add(start);

discovered.add(start);

start.distance = 0;

while (!perimeter.isEmpty()) {

Vertex from = perimeter.remove();

for (E edge : graph.outgoingEdgesFrom(from)) {

Vertex to = edge.to();

if (!discovered.contains(to)) {

to.distance = from.distance + 1;

to.predecessorEdge = edge;

perimeter.add(to);

discovered.add(to)

}

}

}

Changes from traversal BFS:

  • Every node now will have an associated distance (for convenience)
  • Every node V now will have an associated predecessor edge that is the edge that connects V on the shortest path from S to V. The edges that each of the nodes store are the final result.

78 of 81

Unweighted Graphs

  • Use BFS to find shortest paths in this graph.

CSE 373 19 SU - ROBBIE WEBER

perimeter.add(start);

discovered.add(start);

start.distance = 0;

while (!perimeter.isEmpty()) {

Vertex from = perimeter.remove();

for (E edge : graph.outgoingEdgesFrom(from)) {

Vertex to = edge.to();

if (!discovered.contains(to)) {

to.distance = from.distance + 1;

to.predecessorEdge = edge;

perimeter.add(to);

discovered.add(to)

}

}

}

1

2

4

5

6

7

8

0

s

t

79 of 81

Unweighted Graphs

  • Use BFS to find shortest paths in this graph.

CSE 373 19 SU - ROBBIE WEBER

79

1

2

4

5

6

7

8

0

s

t

perimeter.add(start);

discovered.add(start);

start.distance = 0;

while (!perimeter.isEmpty()) {

Vertex from = perimeter.remove();

for (E edge : graph.outgoingEdgesFrom(from)) {

Vertex to = edge.to();

if (!discovered.contains(to)) {

to.distance = from.distance + 1;

to.predecessorEdge = edge;

perimeter.add(to);

discovered.add(to)

}

}

}

If trying to recall the best path from 0 to 5:

5’s predecessor edge is (8, 5)

8’s predecessor edge is (0, 8)

0 was the start vertex

Note: this BFS modification produces these edges, but there’s extra work to figure out a specific path from a start / target

80 of 81

What about the target vertex?

CSE 373 19 SU - ROBBIE WEBER

80

Given: a directed graph G and vertices s,t

Find: the shortest path from s to t.

Shortest Path Problem

BFS didn’t mention a target vertex…

It actually finds the distance from s to every other vertex. The resulting edges are called the shortest path tree.

All our shortest path algorithms have this property.

If you only care about one target, you can sometimes stop early (in bfsShortestPaths, when the target pops off the queue)

81 of 81

Map<V, E> bfsFindShortestPathsEdges(G graph, V start) {

// stores the edge `E` that connects `V` in the shortest path from s to V

  • Map<V, E> edgeToV = empty map

  • // stores the shortest path length from `start` to `V`
  • Map<V, Double> distToV = empty map

  • Queue<V> perimeter = new Queue<>();

Set<V> discovered = new Set<>();

  • // setting up the shortest distance from start to start is just 0 with
  • // no edge leading to it
  • edgeTo.put(start, null);
  • distTo.put(start, 0.0);

  • perimeter.add(start);

  • while (!perimeter.isEmpty()) {
  • V from = perimeter.remove();
  • for (E e : graph.outgoingEdgesFrom(from)) {
  • V to = e.to();
  • if (!discovered.contains(to)) {
  • edgeTo.put(to, e);
  • distTo.put(to, distTo(from) + 1);
  • perimeter.add(to, newDist);
  • discovered.add(to)
  • }
  • }
  • }
  • return edgeToV;
  • }

1

2

4

5

6

7

8

0

s

t

This is an alternative way to implement bfsShortestPaths that has an easier time accessing the actual paths / distances by using Maps