to do list · 2014. 2. 6. · edge cases for dfs/bfs traversal tree edges d b e c a forward edge d...
TRANSCRIPT
Graph Traversal with DFS/BFS
Tyler Moore
CSE 3353, SMU, Dallas, TX
Lecture 6
Some slides created by or adapted from Dr. Kevin Wayne. For more information see
http://www.cs.princeton.edu/~wayne/kleinberg-tardos
Graph Traversal
One of the most fundamental graph problems is to traverse everyedge and vertex in a graph.
For correctness, we must do the traversal in a systematic way so thatwe dont miss anything.
For efficiency, we must make sure we visit each edge at most twice.
Since a maze is just a graph, such an algorithm must be powerfulenough to enable us to get out of an arbitrary maze.
2 / 20
Marking Vertices
The key idea is that we must mark each vertex when we first visit it,and keep track of what have not yet completely explored.
Each vertex will always be in one of the following three states:1 undiscovered the vertex in its initial, virgin state.2 discovered the vertex after we have encountered it, but before we have
checked out all its incident edges.3 processed the vertex after we have visited all its incident edges.
A vertex cannot be processed before we discover it, so over the courseof the traversal the state of each vertex progresses from undiscoveredto discovered to processed.
3 / 20
To Do List
We must also maintain a structure containing all the vertices we havediscovered but not yet completely explored.
Initially, only a single start vertex is considered to be discovered.
To completely explore a vertex, we look at each edge going out of it.For each edge which goes to an undiscovered vertex, we mark itdiscovered and add it to the list of work to do.
Note that regardless of what order we fetch the next vertex toexplore, each edge is considered exactly twice, when each of itsendpoints are explored.
4 / 20
In what order should we process vertices?
1 First-in-first-out: if we use a queue to process discovered vertices, weuse breadth-first search
2 Last-in-first-out: if we use a stack to process discovered vertices, weuse depth-first search
5 / 20
Depth-First Traversal
Undirected Graph
a
b
c
d
ef
Discovered:
a
a
b
b
c
c
d
d
e
e
f
fProcessed:
e
e
d
d
c
c
b
b
f
f
a
a
Depth-First Search Tree
a
b
c
d
e
f
1
23
4
7
5
6
6 / 20
Recursive Depth-First Traversal Code
def r e c d f s (G, s , S=None ) :i f S i s None : S = s e t ( )# I n i t i a l i z e the h i s t o r yS . add ( s ) # We ’ ve v i s i t e d sf o r u i n G[ s ] : # Exp l o r e n e i g hbo r s
i f u i n S : continue# Al r eady v i s i t e d : Sk ipr e c d f s (G, u , S ) # New : Exp l o r e r e c u r s i v e l y
>>> r e c d f s t e s t e d (G, 0)[ 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 ]
7 / 20
Iterative Depth-First Traversal Code
def i t e r d f s (G, s ) :S , Q = s e t ( ) , [ ] # V i s i t e d−s e t and queueQ. append ( s ) # We p lan on v i s i t i n g swhi le Q: # Planned nodes l e f t ?
u = Q. pop ( ) # Get onei f u i n S : continue# Al r eady v i s i t e d ? Sk ip i tS . add ( u ) # We ’ ve v i s i t e d i t nowQ. extend (G[ u ] ) # Schedu l e a l l n e i g hbo r sy i e l d u # Report u as v i s i t e d
>>> l i s t ( i t e r d f s (G, 0 ) )[ 0 , 5 , 7 , 6 , 2 , 3 , 4 , 1 ]
8 / 20
What does the yield command do?
When you execute a function, it normally returns values
Generators only execute code when iterated
Useful when you don’t need to keep the list of values you loop over
Especially helpful when the object you’re iterating over is very large,and you don’t want to keep the entire object in memory
Nice explanation on Stack Overflow: http://stackoverflow.com/questions/231767/the-python-yield-keyword-explained
9 / 20
Breadth-First Traversal
Undirected Graph
a
b
c
d
ef
Discovered:
a
a
b
b
e
e
f
f
c
c
d
dProcessed:
a
a
b
b
e
e
f
f
c
c
d
d
Breadth-First Search Tree
a
b e f
c d
1 2
3
4 6
5
7
10 / 20
Breadth-First Traversal Code
from c o l l e c t i o n s import dequedef i t e r b f s (G, s , S = None ) :
S , Q = s e t ( ) , deque ( ) # V i s i t e d−s e t and queueQ. append ( s ) # We p lan on v i s i t i n g swhi le Q: # Planned nodes l e f t ?
u = Q. p o p l e f t ( ) # Get onei f u i n S : continue# Al r eady v i s i t e d ? Sk ip i tS . add ( u ) # We ’ ve v i s i t e d i t nowQ. extend (G[ u ] ) # Schedu l e a l l n e i g hbo r sy i e l d u # Report u as v i s i t e d
11 / 20
Correctness of Graph Traversal
Every edge and vertex in the connected component is eventuallyvisited. Why?
Suppose it’s not correct, ie. there exists an unvisited vertex A whoseneighbor B was visited.
When B was visited, each of its neighbors was added to the list to beprocessed. Since A is a neighbor of B , it must be visited before thealgorithm completes.
12 / 20
Preorder vs. postorder processing
Many useful algorithms are built on top of BFS or DFS traversal
Frequently, extra code is added either immediately before a node isprocessed (preorder processing) or after a node is processed(postorder processing)
13 / 20
Preorder and postorder processing with BFS
def b f s pp (G, s , S = None ) :S , Q = s e t ( ) , deque ( ) # V i s i t e d−s e t and queueQ. append ( s ) # We p lan on v i s i t i n g swh i l e Q: # Planned nodes l e f t ?
u = Q. p o p l e f t ( ) # Get onei f u i n S : cont inue# Al r eady v i s i t e d ? Sk ip i tp r i n t ’ p r e ’+u # pr e o r d e r p r o c e s s i n g −− aka p r o c e s s v e r t e x e a r l y (ADM)S . add ( u ) # We ’ ve v i s i t e d i t nowQ. extend (G[ u ] ) # Schedu l e a l l n e i g hbo r sy i e l d u # Report u as v i s i t e dp r i n t ’ −−−−−− pos t ’+u # po s t o r d e r p r o c e s s i n g −− aka p r o c e s s v e r t e x l a t e
>>> l i s t ( b f s pp (N, ’ a ’ ) )p re a−−−−−− pos t ap re b−−−−−− pos t bp re e−−−−−− pos t ep re f−−−−−− pos t fp r e c−−−−−− pos t cp re d−−−−−− pos t d[ ’ a ’ , ’ b ’ , ’ e ’ , ’ f ’ , ’ c ’ , ’ d ’ ]
14 / 20
Preorder and postorder processing with DFS
def r e c d f s p p (G, s , S=None ) :i f S i s None : S = s e t ( )# I n i t i a l i z e the h i s t o r yS . add ( s ) # We ’ ve v i s i t e d sp r i n t ’ p r e : ’+s # p r i n t node −− p r e o r d e r p r o c e s s i n gf o r u i n G[ s ] : # Exp l o r e n e i g hbo r s
i f u i n S : cont inue# Al r eady v i s i t e d : Sk ipr e c d f s p p (G, u , S ) # New : Exp l o r e r e c u r s i v e l y
p r i n t ’−−−−−− pos t : ’+s # p r i n t node −− po s t o r d e r p r o c e s s i n gr e t u r n S
>>> l i s t ( r e c d f s p p (N, ’ a ’ ) )p re : ap re : bp re : cp re : dp re : e−−−−−− pos t : e−−−−−− pos t : d−−−−−− pos t : c−−−−−− pos t : bp re : f−−−−−− pos t : f−−−−−− pos t : a[ ’ a ’ , ’ c ’ , ’ b ’ , ’ e ’ , ’ d ’ , ’ f ’ ]
15 / 20
DFS Trees: all descendants of a node u are processed afteru is discovered but before u is processed
Undirected Graph
a
b
c
d
ef
Discovered:
a
a
b
b
c
c
d
d
e
e
f
fProcessed:
e
e
d
d
c
c
b
b
f
f
a
a
Depth-First Search Tree
a
b
c
d
e
f
1
23
4
10
5
6
16 / 20
How can we tell if one node is a descendant of another?
Answer: with depth-first timestamps!
After we create a graph in a depth-first traversal, it would be nice tobe able to verify if node A is encountered before node B , etc.
We add one timestamp for when a node is discovered (during preorderprocessing) and another timestamp for when a node is processed(during postorder processing)
17 / 20
Code for depth-first timestamps
def d f s (G, s , d , f , S=None , t =0):i f S i s None : S = s e t ( )# I n i t i a l i z e the h i s t o r yd [ s ] = t ; t += 1 # Set d i s c o v e r t imeS . add ( s ) # We ’ ve v i s i t e d sf o r u i n G[ s ] : # Exp l o r e n e i g hbo r s
i f u i n S : continue# Al r eady v i s i t e d . Sk ipt = d f s (G, u , d , f , S , t ) # Recur se ; update
f [ s ] = t ; t += 1 # Set f i n i s h t imereturn t # Return t imestamp
>>> f={}>>> d={}>>> d f s (N, ’ a ’ , d , f )12>>> d{ ’ a ’ : 0 , ’ c ’ : 2 , ’ b ’ : 1 , ’ e ’ : 4 , ’ d ’ : 3 , ’ f ’ : 9}>>> f{ ’ a ’ : 11 , ’ c ’ : 7 , ’ b ’ : 8 , ’ e ’ : 5 , ’ d ’ : 6 , ’ f ’ : 10}
18 / 20
Edge cases for DFS/BFS traversal
Tree edges
d
b
e
c
a
Forward edge
d
b
e
c
a
Back edge
d
b
e
c
a
Cross edges
d
b
e
c
a
19 / 20
Edge classification
1 Edge (u, v) is a tree edge if v was first discovered by exploring edge(u, v).
2 Edge (u, v) is a back edge if vertex u is connected to an ancestor v .
3 Edge (u, v) is a forward edge if vertex u is connected to adescendant v .
4 All other edges are cross edges
20 / 20