Depth-first search: Difference between revisions

From Algowiki
Jump to navigation Jump to search
No edit summary
 
(91 intermediate revisions by 4 users not shown)
Line 3: Line 3:
[[Category:Tree Algorithms]]
[[Category:Tree Algorithms]]
[[Category:Graph Traversal]]
[[Category:Graph Traversal]]
== Definition ==
[[Category:Check:Checkup]]


Depth-first search
== General information ==
Algorithmic problem: Graph Traversal
Prerequisites:
Type of algorithm: loop
Auxiliary data:
A stack  whose elements are nodes in .
Each node  has a current arc , which is either  or an outgoing arc  of .
Each node has a Boolean label with semantics "is marked".
Abstract view


Invariant: After  iterations:
'''Algorithmic problem:''' [[Graph traversal]]
The nodes marked so far are the  lexicographically first nodes from .
The arcs traversed in forward direction are the arcs of the tree that is the union of the lexicographically first paths from  to all marked nodes.
Stack  contains the nodes on the lexicographically first path of the node marked last, in the order from  to that node.
For each node ,  is the lexicographically first outgoing arc of  that has not yet been considered for going forward from  (or , if no such arc exists).
Variant:  increases by .
Break condition:  .


Induction basis
'''Type of algorithm:''' loop


Abstract view:
== Definitions ==
has one element: root .
# For each node, an arbitrary but fixed ordering of the outgoing arcs is assumed. An arc <math>(v,w)</math> preceding an arc <math>(v,w')</math> in this ordering is called '''lexicographically smaller''' than <math>(v,w)</math>.
Root  is marked, all other nodes are unmarked.
# Let <math>p</math> and <math>p'</math> be two paths that start from the same node <math>v\in V</math>, but may or may not have the same end node. Let <math>w</math> be the last common node such that the subpaths of <math>p</math> and <math>p'</math> from <math>v</math> up to <math>w</math> are identical (possibly <math>v=w</math>). If the next arc of <math>p</math> from <math>w</math> onwards is lexicographically smaller than the next arc of <math>p'</math> from <math>w</math> onwards, <math>p</math> is said to be '''lexicograpically smaller''' than <math>p'</math>. Note that the lexicographically ''smallest'' path from <math>v\in V</math> to <math>w\in V</math> is well defined and unique.
Implementation: Obvious.
# With respect to a starting node <math>s\in V</math>, a node <math>v\in V</math> is '''lexicographically smaller''' than <math>w\in V</math> if the lexicographically smallest path from <math>s</math> to <math>v</math> is lexicographically smaller than the lexicographically smallest path from <math>s</math> to <math>w</math>.
Proof: Nothing to show.
# In all of the above cases, the reverse relation is called '''lexicographically larger'''.
# A node <math>v\in V</math> is '''lexicographically smaller''' (resp., '''lexicograpically larger''') than a path <math>p</math> if <math>v</math> does not belong to <math>p</math> and the lexicographically smallest path from the start node of <math>p</math> to <math>v</math> is lexicographically smaller (resp., larger) than <math>p</math>. (Note the asymmetry: In both cases, the lexicographically ''smallest'' path to <math>v</math> is used.)
# In all cases, we also say '''precedes''' and '''succeeds''', respectively, instead of "is lexicograpically smaller/larger".


Induction step
== Abstract view ==


Abstract view: If the current node has at least one outgoing arc to an unmarked node, traverse the lexicographically first of them to its target node and mark that node. Otherwise, go back along the path in  until the current node does have such an outgoing arc, and traverse the first such arc in the list.
'''Additional output:'''
Implementation:
# Each node has two Boolean labels with semantics, "is seen" and "is finished".
Let  denote the top element of .
# An [[Basic graph definitions#Forests, trees, branchings, arborescences|arborescence]] <math>A=(V',A')</math> rooted at <math>s</math> such that <math>V'\subseteq V</math> is the set of all nodes reachable from <math>s</math> (including <math>s</math>). For each node <math>v\in V'</math>, the path from <math>s</math> to <math>v</math> in <math>A</math> is the lexicographically smallest <math>(s,v)</math>-path in <math>G</math>.
While :
Pop the top element from .
If , terminate the algorithm.
Let  denote the new top element of .
While  and the target node of  is marked, let the new  be the next arc after  in the list of outgoing arcs of  (or , if no such arc exists).
Push the target node of  onto  and mark it.
Correctness: Since  terminates the main loop, the first step and the first execution of Step 2.1 are well defined: at that moment, there indeed is a top element ready to be read and removed from . Due to Step 2.2, all further executions of Step 2.1 and all executions of Steps 2.3 and 2.4 are well defined, too.
Obviously, the loop in Step 2 goes backwards along the path encoded in  until there is an opportunity  to go forward. Due to the prefix property, always contains the lexicographically first path to the current top element during the backtracking. Since  is not marked,  is the first arc from some lexicographically preceding node to . Therefore, when  is pushed onto , the current contents of  comprises the lexicographically first path to .


Complexity
'''Specific characteristic:'''
The nodes may be returned either in lexicographic order or (alternatively or simultaneously) in '''parenthetical order''', that is:
Let <math>v,w\in V</math> such that <math>v</math> is seen before <math>w</math>. If there is a path from <math>v</math> to <math>w</math>, <math>w</math> is finished prior to <math>v</math>.


Statement: The asymptotic complexity is in  in the worst case, where  and . If all nodes of  are reachable from , it is also  in the best and worst case.
'''Remark:'''
Proof: Note that every node is pushed at most once onto  because a marked (=already pushed) node is skipped in Step 2.4. Therefore, the number of forward steps is in  in the worst case. Clearly, the number of removals from  is in  in the worst case as well. Also note that each arc is considered at most once for going forward because an outgoing arc of  becomes the current arc  at most once. No other operation is executed more often than the stack operations and these arc operations. In summary, this gives  in the worst case.
''Parenthetical'' refers to the following intuition: Whenever a node is seen, open a parenthesis, and close it once the node is finished. The result is a correct parenthetial expression: Two parentheses are either disjoint, or one is nested in the other one.
Each node of  that is reachable from  will be seen by the search. Therefore, if all nodes are reachable, the number of push (and pop) operations is in  in any case. Since a node is only removed from  after all of its outgoing arcs were seen, all arcs will eventually be seen in that case, so the total number of arc inspections is then in .


Further information
'''Auxiliary data:'''
# A [[Sets and sequences#Stacks and queues|stack]] <math>S</math> whose elements are nodes in <math>V</math>.
# Each node  has a '''current arc''' <math>a_v\in V</math>, which is either void or an outgoing arc <math>a_v=(v,w)</math> of <math>v</math>. (May be viewed as an iterator over the list of all outgoing arcs.)


Depth-first search could alternatively be implemented as a more fine-grained loop in which, in each iteration, exactly one forward or backward step over an arc is performed.
'''Invariant:'''
Before and after each iteration:
# <math>S</math> forms a path <math>p</math> from the start node <math>s</math> to some other node, that is, the order of the nodes on <math>p</math> is the order in which they appear in <math>S</math> (start node <math>s</math> at the bottom of <math>S</math>).
# For each node not yet seen, the current arc is the first arc (or void if the node has no outgoing arcs).
# For each node <math>v</math> on <math>p</math>:
## If there are arcs <math>(v,w)\in A</math> such that <math>w</math> is not yet seen, the current arc equals or precedes the first such arc.
## The subpath of <math>p</math> from the start node <math>s</math> to <math>v</math> is the lexicographically first <math>(s,v)</math>-path.
# The nodes on <math>p</math> are seen but not finished. Let <math>p+a</math> denote the concatenation of <math>p</math> with the current arc <math>a</math> of the last node of <math>p</math>. The nodes that are lexicographically smaller than <math>p+a</math> are seen and finished, and the nodes that lexicographically succeed <math>p+a</math> are neither seen nor finished. (Note that nothing is said about the head of <math>a</math>).


== Pseudocode ==  
'''Variant:''' Either one node is seen for the first time or finished, or (inclusive) the current arc of one node is moved forward.
 
'''Break condition:''' <math>S=\emptyset</math>.
 
== Induction basis ==
 
'''Abstract view:''' No node is finished. The start node <math>s</math> is seen, no other node is seen. The start node is the only element of <math>S</math>. The current arc of each node is its first outgoing arc. If the nodes are to be returned in lexicographic order, the start node <math></math> is, initially, the only member of the output sequence; otherwise, the initial output sequence is empty. Arborescence <math>A</math> is initialized so as to contain <math>s</math> and nothing else.
 
'''Implementation:''' Obvious.
 
'''Proof:''' Obvious.
 
== Induction step ==
 
'''Abstract view:'''
# Let <math>v</math> be the last node of <math>p</math> (=the top element of <math>S</math>).
# While the current arc of <math>v</math> is not void and while the head of the current arc is labeled as seen: Move the current arc one step forward.
# If the current arc of <math>v</math> is not void, say, <math>a_v=(v,w)</math>:
## Insert <math>w</math> and <math>(v,w)</math> in <math>A</math>.
## Push <math>w</math> on <math>S</math>.
## Label <math>w</math> as seen.
## If the output order is the lexicographical one: Append <math>w</math> to the output sequence.
# Otherwise:
## Remove <math>v</math> from <math>S</math>
## Label <math>v</math> as finished.
## If the output order is the parenthetical one: Put <math>v</math> in the output sequence.
 
'''Implementation:''' Obvious.
 
'''Proof:'''
The loop ''variant'' is obviously fulfilled.
 
The first point of the ''invariant'' is obviously fulfilled, too. The second point follows from the fact that the current arc of a node is initialized to be the node's very first outgoing arc and only changed after the node is labeled as ''seen''. Point 3.1 follows from the fact that, in step 2, the current arc never skips an arc that points to an unseen node.
 
For point 3.2, let <math>p'</math> be the lexicographically smallest <math>(s,v)</math>-path. Moreover, let <math>w\neq v</math> be the last node on <math>p</math> and <math>p'</math> such that both paths are identical from <math>s</math> to <math>w</math> (possibly, <math>w=s</math>). Further, let <math>u</math> and <math>u'</math> be the immediate successors of <math>w</math> on <math>p</math> and <math>p'</math>, respectively. Then <math>u'</math> has been seen before <math>u</math> because <math>(w,u)</math> is the arc over which <math>u</math> was seen for the first time, and <math>(w,u')</math> precedes <math>(w,u)</math>. Note that <math>v</math> has not been seen earlier than <math>u</math> (in fact, later than <math>u</math>, unless <math>v=u</math>). In summary, <math>u'</math> has been seen before <math>v</math>. Since there is a path from <math>u'</math> to <math>v</math>, the correctness proof [[#Correctness|below]] will prove that <math>v</math> was finished before <math>u'</math>. This contradicts the induction hypothesis (point 4 of the invariant).
 
When a node is pushed on <math>S</math>, it is neither seen nor finished immediately before that iteration and then labeled as seen in that iteration. The node is finished when it leaves <math>S</math>. Both facts together give the first sentence of point 4. The other statements of point 4 follow from the observation that the concatenation of <math>p</math> with the current arc of the endnode of <math>p</math> increases lexicographically in each iteration in which Step 3 applies.
 
== Correctness ==
 
It is easy to see that each operation of the algorithm is well defined. Due to the variant, the loop terminates after a finite number of steps. Immediately before the last iteration, <math>p</math> consists of the start node <math>s</math> only, and the current arc of <math>s</math> is void. Therefore, ''all'' nodes reachable from <math>s</math> except for <math>s</math> itself are lexicographically smaller than <math>p</math> at that moment. Due to point 4 of the invariant, all of these nodes are finished. In the last iteration, <math>s</math> is finished as well.
 
So, it remains to show that the specific characteristic is fulfilled in both cases.
Due to point 3.2 of the invariant, a node is seen for the first time via its lexicographically smallest path from <math>s</math>. Since the current path increases lexicographically in each iteration, the nodes are labeled as seen in lexicographic order. In summary, the lexicographical order is correct.
 
So consider the second, the parenthetical case.
Let <math>v</math> be seen before <math>w</math> and assume there is a path from <math>v</math> to <math>w</math>. We have to show that <math>w</math> is finished prior to <math>v</math>. Let <math>p'</math> denote the lexicographically smallest <math>(v,w)</math>-path. There is a stage in which this path is a subpath of the current path <math>p</math>. Clearly, <math>v</math> cannot be removed from <math>S</math> before <math>w</math>. This proves the claim.
 
== Complexity ==
 
'''Statement:''' The asymptotic complexity is in <math>\Theta(|V|+|A|)</math> in the worst case.
 
'''Proof:'''
For every node reachable from <math>s</math> (including <math>s</math>), the algorithm processes each of its outgoing arcs exactly once. And from each of these nodes, the algorithm goes backwards exactly once. Obviously, each of these steps requires a constant number of operations.
 
== Remark ==
 
Alternatively, DFS could be implemented as a recursive procedure. However, this excludes the option to implement DFS as an iterator, which means to turn the loop inside-out (cf. remarks on [[Graph traversal|graph traversal]]).
 
== Pseudocode recursive implementation ==  




Line 78: Line 127:
:''color''[''u''] &larr; BLACK
:''color''[''u''] &larr; BLACK
:''f''[''u''] &larr; ''time'' &larr; ''time'' + 1
:''f''[''u''] &larr; ''time'' &larr; ''time'' + 1
== Pseudocode stack implementation ==
====DFS(''s'')====
: ''S = new Stack()''
: ''s.IsSeen = true''
: ''S.push(s)''
: '''while''' ''S'' &ne; &empty;
:: ''n'' = ''S.peek()''
:: ''a'' = ''(v, w)'' = ''n.nextArc()''
:: '''if''' ''a'' == ''null''
::: ''n.IsFinised'' = ''true''
::: ''S.pop()''
:: '''else if''' ''w.IsSeen'' == ''false''
::: ''w.IsSeen'' = ''true''
::: ''S.push(w)''

Latest revision as of 06:52, 26 October 2015


General information

Algorithmic problem: Graph traversal

Type of algorithm: loop

Definitions

  1. For each node, an arbitrary but fixed ordering of the outgoing arcs is assumed. An arc [math]\displaystyle{ (v,w) }[/math] preceding an arc [math]\displaystyle{ (v,w') }[/math] in this ordering is called lexicographically smaller than [math]\displaystyle{ (v,w) }[/math].
  2. Let [math]\displaystyle{ p }[/math] and [math]\displaystyle{ p' }[/math] be two paths that start from the same node [math]\displaystyle{ v\in V }[/math], but may or may not have the same end node. Let [math]\displaystyle{ w }[/math] be the last common node such that the subpaths of [math]\displaystyle{ p }[/math] and [math]\displaystyle{ p' }[/math] from [math]\displaystyle{ v }[/math] up to [math]\displaystyle{ w }[/math] are identical (possibly [math]\displaystyle{ v=w }[/math]). If the next arc of [math]\displaystyle{ p }[/math] from [math]\displaystyle{ w }[/math] onwards is lexicographically smaller than the next arc of [math]\displaystyle{ p' }[/math] from [math]\displaystyle{ w }[/math] onwards, [math]\displaystyle{ p }[/math] is said to be lexicograpically smaller than [math]\displaystyle{ p' }[/math]. Note that the lexicographically smallest path from [math]\displaystyle{ v\in V }[/math] to [math]\displaystyle{ w\in V }[/math] is well defined and unique.
  3. With respect to a starting node [math]\displaystyle{ s\in V }[/math], a node [math]\displaystyle{ v\in V }[/math] is lexicographically smaller than [math]\displaystyle{ w\in V }[/math] if the lexicographically smallest path from [math]\displaystyle{ s }[/math] to [math]\displaystyle{ v }[/math] is lexicographically smaller than the lexicographically smallest path from [math]\displaystyle{ s }[/math] to [math]\displaystyle{ w }[/math].
  4. In all of the above cases, the reverse relation is called lexicographically larger.
  5. A node [math]\displaystyle{ v\in V }[/math] is lexicographically smaller (resp., lexicograpically larger) than a path [math]\displaystyle{ p }[/math] if [math]\displaystyle{ v }[/math] does not belong to [math]\displaystyle{ p }[/math] and the lexicographically smallest path from the start node of [math]\displaystyle{ p }[/math] to [math]\displaystyle{ v }[/math] is lexicographically smaller (resp., larger) than [math]\displaystyle{ p }[/math]. (Note the asymmetry: In both cases, the lexicographically smallest path to [math]\displaystyle{ v }[/math] is used.)
  6. In all cases, we also say precedes and succeeds, respectively, instead of "is lexicograpically smaller/larger".

Abstract view

Additional output:

  1. Each node has two Boolean labels with semantics, "is seen" and "is finished".
  2. An arborescence [math]\displaystyle{ A=(V',A') }[/math] rooted at [math]\displaystyle{ s }[/math] such that [math]\displaystyle{ V'\subseteq V }[/math] is the set of all nodes reachable from [math]\displaystyle{ s }[/math] (including [math]\displaystyle{ s }[/math]). For each node [math]\displaystyle{ v\in V' }[/math], the path from [math]\displaystyle{ s }[/math] to [math]\displaystyle{ v }[/math] in [math]\displaystyle{ A }[/math] is the lexicographically smallest [math]\displaystyle{ (s,v) }[/math]-path in [math]\displaystyle{ G }[/math].

Specific characteristic: The nodes may be returned either in lexicographic order or (alternatively or simultaneously) in parenthetical order, that is: Let [math]\displaystyle{ v,w\in V }[/math] such that [math]\displaystyle{ v }[/math] is seen before [math]\displaystyle{ w }[/math]. If there is a path from [math]\displaystyle{ v }[/math] to [math]\displaystyle{ w }[/math], [math]\displaystyle{ w }[/math] is finished prior to [math]\displaystyle{ v }[/math].

Remark: Parenthetical refers to the following intuition: Whenever a node is seen, open a parenthesis, and close it once the node is finished. The result is a correct parenthetial expression: Two parentheses are either disjoint, or one is nested in the other one.

Auxiliary data:

  1. A stack [math]\displaystyle{ S }[/math] whose elements are nodes in [math]\displaystyle{ V }[/math].
  2. Each node has a current arc [math]\displaystyle{ a_v\in V }[/math], which is either void or an outgoing arc [math]\displaystyle{ a_v=(v,w) }[/math] of [math]\displaystyle{ v }[/math]. (May be viewed as an iterator over the list of all outgoing arcs.)

Invariant: Before and after each iteration:

  1. [math]\displaystyle{ S }[/math] forms a path [math]\displaystyle{ p }[/math] from the start node [math]\displaystyle{ s }[/math] to some other node, that is, the order of the nodes on [math]\displaystyle{ p }[/math] is the order in which they appear in [math]\displaystyle{ S }[/math] (start node [math]\displaystyle{ s }[/math] at the bottom of [math]\displaystyle{ S }[/math]).
  2. For each node not yet seen, the current arc is the first arc (or void if the node has no outgoing arcs).
  3. For each node [math]\displaystyle{ v }[/math] on [math]\displaystyle{ p }[/math]:
    1. If there are arcs [math]\displaystyle{ (v,w)\in A }[/math] such that [math]\displaystyle{ w }[/math] is not yet seen, the current arc equals or precedes the first such arc.
    2. The subpath of [math]\displaystyle{ p }[/math] from the start node [math]\displaystyle{ s }[/math] to [math]\displaystyle{ v }[/math] is the lexicographically first [math]\displaystyle{ (s,v) }[/math]-path.
  4. The nodes on [math]\displaystyle{ p }[/math] are seen but not finished. Let [math]\displaystyle{ p+a }[/math] denote the concatenation of [math]\displaystyle{ p }[/math] with the current arc [math]\displaystyle{ a }[/math] of the last node of [math]\displaystyle{ p }[/math]. The nodes that are lexicographically smaller than [math]\displaystyle{ p+a }[/math] are seen and finished, and the nodes that lexicographically succeed [math]\displaystyle{ p+a }[/math] are neither seen nor finished. (Note that nothing is said about the head of [math]\displaystyle{ a }[/math]).

Variant: Either one node is seen for the first time or finished, or (inclusive) the current arc of one node is moved forward.

Break condition: [math]\displaystyle{ S=\emptyset }[/math].

Induction basis

Abstract view: No node is finished. The start node [math]\displaystyle{ s }[/math] is seen, no other node is seen. The start node is the only element of [math]\displaystyle{ S }[/math]. The current arc of each node is its first outgoing arc. If the nodes are to be returned in lexicographic order, the start node [math]\displaystyle{ }[/math] is, initially, the only member of the output sequence; otherwise, the initial output sequence is empty. Arborescence [math]\displaystyle{ A }[/math] is initialized so as to contain [math]\displaystyle{ s }[/math] and nothing else.

Implementation: Obvious.

Proof: Obvious.

Induction step

Abstract view:

  1. Let [math]\displaystyle{ v }[/math] be the last node of [math]\displaystyle{ p }[/math] (=the top element of [math]\displaystyle{ S }[/math]).
  2. While the current arc of [math]\displaystyle{ v }[/math] is not void and while the head of the current arc is labeled as seen: Move the current arc one step forward.
  3. If the current arc of [math]\displaystyle{ v }[/math] is not void, say, [math]\displaystyle{ a_v=(v,w) }[/math]:
    1. Insert [math]\displaystyle{ w }[/math] and [math]\displaystyle{ (v,w) }[/math] in [math]\displaystyle{ A }[/math].
    2. Push [math]\displaystyle{ w }[/math] on [math]\displaystyle{ S }[/math].
    3. Label [math]\displaystyle{ w }[/math] as seen.
    4. If the output order is the lexicographical one: Append [math]\displaystyle{ w }[/math] to the output sequence.
  4. Otherwise:
    1. Remove [math]\displaystyle{ v }[/math] from [math]\displaystyle{ S }[/math]
    2. Label [math]\displaystyle{ v }[/math] as finished.
    3. If the output order is the parenthetical one: Put [math]\displaystyle{ v }[/math] in the output sequence.

Implementation: Obvious.

Proof: The loop variant is obviously fulfilled.

The first point of the invariant is obviously fulfilled, too. The second point follows from the fact that the current arc of a node is initialized to be the node's very first outgoing arc and only changed after the node is labeled as seen. Point 3.1 follows from the fact that, in step 2, the current arc never skips an arc that points to an unseen node.

For point 3.2, let [math]\displaystyle{ p' }[/math] be the lexicographically smallest [math]\displaystyle{ (s,v) }[/math]-path. Moreover, let [math]\displaystyle{ w\neq v }[/math] be the last node on [math]\displaystyle{ p }[/math] and [math]\displaystyle{ p' }[/math] such that both paths are identical from [math]\displaystyle{ s }[/math] to [math]\displaystyle{ w }[/math] (possibly, [math]\displaystyle{ w=s }[/math]). Further, let [math]\displaystyle{ u }[/math] and [math]\displaystyle{ u' }[/math] be the immediate successors of [math]\displaystyle{ w }[/math] on [math]\displaystyle{ p }[/math] and [math]\displaystyle{ p' }[/math], respectively. Then [math]\displaystyle{ u' }[/math] has been seen before [math]\displaystyle{ u }[/math] because [math]\displaystyle{ (w,u) }[/math] is the arc over which [math]\displaystyle{ u }[/math] was seen for the first time, and [math]\displaystyle{ (w,u') }[/math] precedes [math]\displaystyle{ (w,u) }[/math]. Note that [math]\displaystyle{ v }[/math] has not been seen earlier than [math]\displaystyle{ u }[/math] (in fact, later than [math]\displaystyle{ u }[/math], unless [math]\displaystyle{ v=u }[/math]). In summary, [math]\displaystyle{ u' }[/math] has been seen before [math]\displaystyle{ v }[/math]. Since there is a path from [math]\displaystyle{ u' }[/math] to [math]\displaystyle{ v }[/math], the correctness proof below will prove that [math]\displaystyle{ v }[/math] was finished before [math]\displaystyle{ u' }[/math]. This contradicts the induction hypothesis (point 4 of the invariant).

When a node is pushed on [math]\displaystyle{ S }[/math], it is neither seen nor finished immediately before that iteration and then labeled as seen in that iteration. The node is finished when it leaves [math]\displaystyle{ S }[/math]. Both facts together give the first sentence of point 4. The other statements of point 4 follow from the observation that the concatenation of [math]\displaystyle{ p }[/math] with the current arc of the endnode of [math]\displaystyle{ p }[/math] increases lexicographically in each iteration in which Step 3 applies.

Correctness

It is easy to see that each operation of the algorithm is well defined. Due to the variant, the loop terminates after a finite number of steps. Immediately before the last iteration, [math]\displaystyle{ p }[/math] consists of the start node [math]\displaystyle{ s }[/math] only, and the current arc of [math]\displaystyle{ s }[/math] is void. Therefore, all nodes reachable from [math]\displaystyle{ s }[/math] except for [math]\displaystyle{ s }[/math] itself are lexicographically smaller than [math]\displaystyle{ p }[/math] at that moment. Due to point 4 of the invariant, all of these nodes are finished. In the last iteration, [math]\displaystyle{ s }[/math] is finished as well.

So, it remains to show that the specific characteristic is fulfilled in both cases. Due to point 3.2 of the invariant, a node is seen for the first time via its lexicographically smallest path from [math]\displaystyle{ s }[/math]. Since the current path increases lexicographically in each iteration, the nodes are labeled as seen in lexicographic order. In summary, the lexicographical order is correct.

So consider the second, the parenthetical case. Let [math]\displaystyle{ v }[/math] be seen before [math]\displaystyle{ w }[/math] and assume there is a path from [math]\displaystyle{ v }[/math] to [math]\displaystyle{ w }[/math]. We have to show that [math]\displaystyle{ w }[/math] is finished prior to [math]\displaystyle{ v }[/math]. Let [math]\displaystyle{ p' }[/math] denote the lexicographically smallest [math]\displaystyle{ (v,w) }[/math]-path. There is a stage in which this path is a subpath of the current path [math]\displaystyle{ p }[/math]. Clearly, [math]\displaystyle{ v }[/math] cannot be removed from [math]\displaystyle{ S }[/math] before [math]\displaystyle{ w }[/math]. This proves the claim.

Complexity

Statement: The asymptotic complexity is in [math]\displaystyle{ \Theta(|V|+|A|) }[/math] in the worst case.

Proof: For every node reachable from [math]\displaystyle{ s }[/math] (including [math]\displaystyle{ s }[/math]), the algorithm processes each of its outgoing arcs exactly once. And from each of these nodes, the algorithm goes backwards exactly once. Obviously, each of these steps requires a constant number of operations.

Remark

Alternatively, DFS could be implemented as a recursive procedure. However, this excludes the option to implement DFS as an iterator, which means to turn the loop inside-out (cf. remarks on graph traversal).

Pseudocode recursive implementation

DFS(G)

for each vertex uV [G]
do color[u] ← WHITE
π[u] ← NIL
time ← 0
do if color[u] == WHITE
then DFS-VISiT(u)


DFS-VISIT(u)

color[u] ← GRAY
timetime + 1
d[u] ← time
for each vAdj[u]
do if color[v] = WHITE
then π [v] ← u
DFS-VISIT(v)
color[u] ← BLACK
f[u] ← timetime + 1


Pseudocode stack implementation

DFS(s)

S = new Stack()
s.IsSeen = true
S.push(s)
while S ≠ ∅
n = S.peek()
a = (v, w) = n.nextArc()
if a == null
n.IsFinised = true
S.pop()
else if w.IsSeen == false
w.IsSeen = true
S.push(w)