2

Is it possible to get a DFS doing a BFS search? If I just use a stack and then pop them out wouldn't it come out in DFS order?

I am trying to do a DFS search but I only have an adjacency list with outgoing edges, so I don't know how to get the indegree of every vertex.

0: 2,4 1: none 2: 1,3 3: none 4: none

so 0 has outgoing edges to 2 and 4.

I'm kinda lost and thought if I did BFS search using a stack I would get a DFS and then the topological order.

  • Pretty sure this isn't possible. Think about a tree with just two long branches. Dfs would return both branches "in order", bfs would return the nodes ordered by closeness from the root. – sircodesalot Mar 11 '15 at 17:15
  • Related (or maybe dupe?) [How to implement depth first search for graph with non-recursive aprroach](http://stackoverflow.com/q/21508765/572670) – amit Mar 11 '15 at 17:28

2 Answers2

2

When done iteratively, the only difference between DFS and BFS is the data structure used to store to the vertices that will be processed in future iterations.

Use a queue and you get BFS, use a stack and you get DFS.

def bfs(g, start):
   discovered = [False] * (len(g) + 1)
   processed = [False] * (len(g) + 1)
   parents = [-1] * (len(g) + 1)
   discovered[start] = True
   q = deque()   #Different line
   q.append(start)
   while len(q) > 0:
    v = q.popleft() #Different line
    print "Visited:" + str(v)
    # process_ve(v)
    nbors = g[v]
    for n in nbors:
        if not p[n]:
            # process_edge((v,n))
            print ((v, n))
        if not discovered[n]:
            discovered[n] = True
            parents[n] = v
            q.append(n)
    # process_vl(v)
    processed[v] = True
    return parents

And DFS:

def dfs(g, start):
   discovered = [False] * (len(g) + 1)
   processed = [False] * (len(g) + 1)
   parents = [-1] * (len(g) + 1)
   discovered[start] = True
   q = list()  #Different line
   q.append(start)
   while len(q) > 0:
    v = q.pop()  #Different line
    print "Visited:" + str(v)
    # process_ve(v)
    nbors = g[v]
    for n in nbors:
        if not processed[n]:
            # process_edge((v,n))
            print ((v, n))
        if not discovered[n]:
            d[n] = True
            pts[n] = v
            q.append(n)
    # process_vl(v)
    processed[v] = True
    return parents

The examples in Python above should make that clear. The only difference between them is that we use a deque (Python's version of queue) in one and a list(Python's version of a stack) in the other.

The examples follow the algorithms as explained in The Algorithm Design Manual by Steve Skiena.

aa333
  • 2,556
  • 16
  • 23
0

If you take a classic BFS algorithm and replace the FIFO queue with a LIFO stack, you will indeed get DFS vertex discovery order - so called DFS pre-ordering. I.e. the order in which your algorithm will visit graph vertices for the very first time will be exactly the same as in DFS.

However, this will not give you DFS memory usage and will not give you post-ordering of vertices - other extremely important properties of the classic DFS algorithm (Why is depth-first search claimed to be space efficient?)

In other words, it is completely incorrect to claim that replacing the BFS queue with a stack will turn BFS into DFS. These algorithms in their canonical form have completely different internal structures. What you will obtain through such replacement is a pseudo-DFS algorithm, which will successfully simulate DFS pre-ordering, but will not provide you with DFS post-ordering.

So it is a question of what you are trying to use it for. Do you need DFS post-ordering?

And it looks like you actually do. The classic toposort algorithm is based specifically on using the vertex post-ordering generated by DFS. The toposort order is the order in which the classic DFS algorithm makes its very last visit to each vertex. This immediately means that your pseudo-DFS will not work for that purpose.

There's probably some way to "whip it into submission" and somehow make it work by adding bunch of crutches here and there, but it is simply not worth it. Just take the classic genuine DFS and use it. It will do it in much more simple, elegant and efficient manner.

Community
  • 1
  • 1
AnT stands with Russia
  • 312,472
  • 42
  • 525
  • 765
  • 1
    Iterative DFS is still a DFS. – amit Mar 11 '15 at 17:35
  • 1
    @amit: I don't know what exactly you mean by "iterative DFS" here. DFS is a classic algorithm with some classic properties. "Backtracking" in my case is not a reference to recursion but a reference to the *last visit order* in the DFS algorithm. – AnT stands with Russia Mar 11 '15 at 17:37
  • 1
    DFS is defined by the order you "extend/visit" nodes. An iterative implementation of DFS is identical to a recursive one when edges are a set (they usually are), and can be easily modified even if they are ordered. – amit Mar 11 '15 at 17:42
  • 1
    @amit: Absolutely not! DFS is defined by its ability to generate both the pre-ordering and post-ordering of the vertices. The toposort algorithm is critically based on DF's ability to generate post-ordering. A BFS with LIFO does not generate post-ordering, which is why it does not qualify as DFS. – AnT stands with Russia Mar 11 '15 at 17:45
  • 1
    Also, DFS is defined by the fact that its peak memory usage is determined by the maximum depth of the search, while in BFS it is determined by the maximum breath of the search. This is also or critical importance. – AnT stands with Russia Mar 11 '15 at 18:02
  • I think the vertex discovery order is what makes DFS, DFS. How the bookkeeping is setup is very different when you do it iteratively (rather than recursively), but the fact that you're visiting vertices in a depth-first way is what make it DFS. – Greg Schmit Sep 17 '18 at 05:49
  • 1
    @GregSchmit: Well, if *discovery* order is all you care about, then everything else might not matter. But the rest is not mere "bookkeeping". In general case vertex "undiscovery" order is as important as vertex "discovery" order. The situation is perfectly symmetrical. Again, one of the most often quoted examples of DFS-based algorithm is topological sorting. Toposort piggybacks on DFS backtracking, not on DFS discovery. Classic Dijkstra's DFS works perfectly for toposort, but the above pseudo-DFS is completely unsuitable for this application. – AnT stands with Russia Sep 17 '18 at 06:45
  • No, what I'm saying is that iterative DFS is perfectly suitable for getting the "right" undiscovery order. It requires different bookkeeping, but the time/space complexity is the same as the recursive DFS. It's just a little weird. And the distinction between discovered and visited nodes is significantly more important. The example below shows that (the important piece is the `for n in nbors` block, where for BFS you just ensure that node wasn't `discovered`, but for DFS iterative, you track both `processed` and `discovered`. – Greg Schmit Sep 17 '18 at 16:07