3

Until April 2nd, the pseudocode on Wikipedia for iterative depth-first search was the following:

procedure DFS_iterative(G, v) is
    let S be a stack
    S.push(v)
    while S is not empty do
        v = S.pop()
        if v is not labeled as discovered then
            label v as discovered
            for all edges from v to w in G.adjacentEdges(v) do
                if w is not labeled as discovered then
                    S.push(w)

On April 2nd, I noticed what I thought was an inconsistency between this pseudocode and the rest of the text of the Wikipedia article (although I think I was partly mistaken), and edited the rest of the text. That edit was reverted by David Eppstein, who also removed the second-to-last-line of the pseudocode (the second discovered check, if w is not labeled as discovered then), so that the pseudocode on Wikipedia currently reads (as of today, April 7th):

procedure DFS_iterative(G, v) is
    let S be a stack
    S.push(v)
    while S is not empty do
        v = S.pop()
        if v is not labeled as discovered then
            label v as discovered
            for all edges from v to w in G.adjacentEdges(v) do 
                S.push(w)

This was done for consistency with the referenced source, Algorithm Design by Kleinberg and Tardos.

Q: Does this edit matter and if so how? Was the previous version of the pseudocode incorrect in some subtle way?

As far as I can tell, both versions of the pseudocode explore the vertices in the same order. Both versions can also produce duplicate copies of vertices on the stack, although the second version produces more duplicates. If you want to add the ability to record parent-child relationships in order to build the DFS tree, I think you have to do slightly different things to each version. Is that all correct?

For context, I am proofreading and editing educational material on DFS for my employer, which uses pseudocode equivalent to the first version, with the two discovered checks. If there's some mistake in this version I'd like to know what it is so that the educational material can be corrected accordingly.

Qiaochu Yuan
  • 131
  • 2

1 Answers1

1

Case $1$: we only push a neighbor $w$ onto the stack if it is not discovered.

Case $2$: we push the neighbor regardless.

If $v$ in the outer-if is already discovered, in both the cases we do nothing and continue. Say we are just discovering $v$. Then, in both the cases we push non-discovered neighbors of $v$. In the second case we additionally push already discovered neighbors of $v$; let us call this set of vertices $S_v$. Whenever something from $S_v$ is popped out as mentioned earlier nothing happens (we don't push anything to the stack). Thus, these pushes are "redundant". Every vertex is discovered exactly once, so redundant pushes onto stack can happen at most $n$ (number of vertices in graph) times, and each time at most $degree(v)$ redundant pushes happen. Thus, the second case is the same as the first one, complexity wise.

advocateofnone
  • 3,179
  • 1
  • 27
  • 44