12

Ok this is my first post on Stack Overflow I have been reading for a little while and really admire the site. I am hoping this is something that will be acceptable to ask. So I have been reading through Intro to Algorithms (Cormen. MIT Press) all the way through and I am up to the graph algorithms. I have been studying the formal algorithms laid out for breadth and depth first search in very fine detail.

Here is the psuedo-code given for depth-first search:

DFS(G)
-----------------------------------------------------------------------------------
1  for each vertex u ∈ G.V
2      u.color ← WHITE       // paint all vertices white; undiscovered
3      u.π ← NIL
4      time ← 0              // global variable, timestamps
5  for each vertex u ∈ G.V
6      if u.color = WHITE
7          DFS-VISIT(G,u)

DFS-VISIT(G, u)
-----------------------------------------------------------------------------------
1  u.color ← GRAY          // grey u; it is discovered
2  time ← time + 1 
3  u.d ← time
4  for each v ∈ G.Adj[u]   // explore edge (u,v)
5      if v.color == WHITE
6          v.π ← u
7          DFS-VISIT(G,v) 
8  u.color ← BLACK         // blacken u; it is finished
9  time ← time + 1
10 u.f ← time

This algorithm is recursive and it proceeds from multiple sources (will discovered every vertex in unconnected graph). It has several properties that most language specific implementations might leave out. It distinguishes between 3 different 'colors' of vertices. It initially paints all of them white, then when they are 'discovered' (visited in DFS-VISIT) they are then painted gray. The DFS-VISIT algorithm runs a loop recursively calling itself on the adjacency list of the current vertex and only paints a vertex black when it has no more edges to any white node.

Also two other attributes of each vertex are maintained u.d and u.f are the time stamps to when the vertex was discovered (painted gray) or when a vertex is finished (painted black). The first time a node is painted it has a time stamp of one and it is incremented to the next integer value for each time another is painted (whether it be grey or black). u.π is also maintained which is just a pointer to the node from which u was discovered.

Algorithm Non-Recursive-DFS(G)
-----------------------------------------------------------------------------------
1   for each vertex u ∈ G.V
2       u.color ← WHITE
3       u.π ← NIL
4   time = 0
5   for each vertex u ∈ G.V
6       if u.color = WHITE
7           u.color ← GRAY
8           time ← time + 1
9           u.d ← time
7           push(u, S)
8           while stack S not empty
9               u ← pop(S)
10              for each vertex v ∈ G.Adj[u]
11                  if v.color = WHITE
12                      v.color = GRAY
13                      time ← time + 1
14                      v.d ← time
15                      v.π ← u
16                      push(v, S)
17              u.color ← BLACK 
18              time ← time + 1
19              u.f ← time

* EDIT 10/31/12 * This is embarrassing that my algorithm has been incorrect for so long, it would work in most cases, but not all. I just got a popular question badge for the question and I saw where Irfy had spotted the problem in his answer below, so that is where the credit goes. I am just fixing it here for anyone that needs this in the future.

Does anyone see a flaw with the above algorithm? I am by far no expert on graph theory, but I think I have a pretty good grasp on recursion and iteration and I believe this should work the same. I am looking to make it functionally equivalent to the recursive algorithm. It should maintain all the attributes of the first algorithm even if they are not needed.

When I began writing it I did not think I would have a triply loops but that's the way it turned out. I have seen other iterative algorithms when I looked around Google that only have a doubly nested loop, however, they do not appear to proceed from multiple sources. (i.e. they will not discover every vertex of unconnected graph)

Cory Gross
  • 36,833
  • 17
  • 68
  • 80
  • Please correct indentation after `if` statement in second loop in second algorithm. Ideally also the indentation after first `for` loop – Irfy Apr 26 '12 at 22:33
  • Made the edit just now, this is what I had originally. – Cory Gross Apr 26 '12 at 22:56
  • 2
    The finish times computed by the non-recursive version will be wrong. u.f <-time can only be set after all descendants have their finish time set. If one takes the same example in CLRS, then the node u would have a finish time=3, whereas the actual value should have been 8. – grdvnl May 19 '13 at 12:00
  • The calculation of finish time is wrong, please correct that. – Ninja420 Sep 09 '13 at 14:03
  • CLRS states that each vertex is initially white until it is first examined (discovered) at which point it is grayed and `u.d` is set to the new time. It remains gray until it's adjacency list is examined completely (u.d is set on all adjacent nodes), at which point it is blackend and the finish time `u.f` is set to the new time. – Cory Gross Sep 09 '13 at 19:27
  • I'm still not sure if this is correct, if anyone knows how to fix it please submit an answer with the changed algorithm. – Cory Gross Sep 09 '13 at 19:31
  • @CoryGross see this http://ideone.com/zw6pNj. – Ravi Upadhyay Nov 08 '13 at 12:27
  • The key points you need to understand are at line 25 and 29. If you can't understand it I will elaborate it. – Ravi Upadhyay Nov 08 '13 at 12:29

8 Answers8

6

Both algorithms are fine. The second one is a direct translation from recursive to stack-based. All mutating state are stored in the stack. G never changes during the execution of the algorithm.

The algorithms will produce a spanning tree for each disconnected region, based on the order the algorithm visited each node. The trees will be represented both with references to the parent-nodes (u.π), and as segment trees (u.d and u.f).

A child will have a reference to its parent node (or NULL if it is a root), as well as having it's range (child.d .. child.f) contained within it's parent's range.

parent.d < child.d < child.f < parent.f

child.π = parent

Edit: I found a mistake in the translation. You are not actually pushing the current state into the stack, but a future method argument. In addition, you are not coloring the popped nodes GRAY and setting the f field.

Here is a rewrite of the original first algorithm:

algorithm Stack-DFS(G)
    for each vertex u ∈ G.V
        u.color ← WHITE
        u.π ← NIL
    time ← 0
    S ← empty stack
    for each vertex u ∈ G.V
        if u.color = WHITE
            # Start of DFS-VISIT
            step ← 1
            index ← 0
            loop unconditionally
                if step = 1
                    # Before the loop
                    u.color ← GRAY
                    time ← time + 1
                    u.d ← time
                    step ← 2
                if step = 2
                    # Start/continue looping
                    for each vertex v ∈ G.Adj[u]
                        i ← index of v
                        if i ≥ index and v.color = WHITE
                            v.π ← u
                            # Push current state
                            push((u, 2, i + 1), S)
                            # Update variables for new call
                            u = v
                            step ← 1
                            index ← 0
                            # Make the call
                            jump to start of unconditional loop
                    # No more adjacent white nodes
                    step ← 3
                if step = 3
                    # After the loop
                    u.color ← BLACK
                    time ← time + 1
                    u.right ← time
                    # Return
                    if S is empty
                        break unconditional loop
                    else
                        u, step, index ← pop(S)

There is probably a few places that could be optimized, but should at least work now.

Result:

Name   d    f   π
q      1   16   NULL
s      2    7   q
v      3    6   s
w      4    5   v
t      8   15   q
x      9   12   t
z     10   11   x
y     13   14   t
r     17   20   NULL
u     18   19   r
Markus Jarderot
  • 86,735
  • 21
  • 136
  • 138
  • his made perfect sense to me and it is a helpful bit of information but after some thought I am not sure it is true in EVERY case, definitely most. Please correct me if I'm wrong as I am trying to learn all I can about this, but I worked out this example here: [link](http://dl.dropbox.com/u/47119048/dfs-ex2.png) and it seems that y's parent is t, and it does not follow the inequality you gave. (This example with vertices in the loops being picked in alphabetical order. – Cory Gross Apr 27 '12 at 01:17
  • Markus, you have a very good point in recognizing the segment trees, which led me to realize the algorithm is incorrect. See my post above. – Irfy Apr 27 '12 at 04:30
1
int stackk[100];
int top=-1;
void graph::dfs(int v){
 stackk[++top]=v;
// visited[v]=1;
 while(top!=-1){
   int x=stackk[top--];
   if(!visited[x])
    {visited[x]=1;
     cout<<x<<endl;
    }
   for(int i=V-1;i>=0;i--)
   {
        if(!visited[i]&&adj[x][i])
        {   //visited[i]=1;
            stackk[++top]=i;
        }
   }
 }
}
void graph::Dfs_Traversal(){
 for(int i=0;i<V;i++)
  visited[i]=0;
 for(int i=0;i<V;i++)
  if(!visited[i])
    g.dfs(i);
hemant
  • 11
  • 1
  • 1
    While this may answer the question, it may be useful to provide some information about the code you posted (either in comments or as a separate explanation). – Hooked Dec 31 '13 at 17:03
  • Agree with @Hooked, it doesn't seem like author of the question asks for an implementation. If you think that it can help, it would be nice to have some text explaining how exactly. – xaizek Dec 31 '13 at 17:09
1

I think I managed to write a much simpler pseudo-code.

but first a few remarks to make things a bit clear:

  1. v.pDescendant - a pointer to a vertex descendant given by its adjacency list.
  2. in the adjacency list, i assumed each element has a field "pNext" that points to the next element on the linked list.
  3. I've used some C++ syntax, mainly "->" and "&" to emphasize what is a pointer and what is not.
  4. Stack.top() returns a pointer to the first element of the stack. but unlike pop(), it does not remove it.

The algorithm is based on the following observation: a vertex is pushed into the stack when visited. and removed (popped) only when we are done examining (blackening) all its descendants.

DFS(G)
1. for all vertices v in G.V do
2.   v.color = WHITE; v.parent = NIL; v.d = NIL; v.f = NIL; v.pDescendant = adj[v].head
3. time = 0 
4. Initialize Stack
5. for all vertices v in G.V s.t. v.color == WHITE do
6.   time++
7.   Stack.push(&v)
8.   v.color = GRAY 
9.   v.d = time 
10.   DFS-ITERATIVE(G,v)

DFS-ITERATIVE(G,s) 
1. while Stack.Empty() == FALSE do
2.   u = Stack.top();
3.   if u.pDescendant == NIL // no Descendants to u || no more vertices to explore
4.      u.color = BLACK
5.      time++
6.      u.f = time
7.      Stack.pop()
8.   else if (u.pDescendant)->color == WHITE
9.      Stack.push(u.pDescendant)
10.     time++
10.     (u.pDescendant)->d = time
11.     (u.pDescendant)->color = GRAY
12.     (u.pDescendant)->parent = &u
12.     u.pDescendant= (u.pDescendant)->pNext // point to next descendant on the adj list      
13.  else
14.     u.pDescendant= (u.pDescendant)->pNext // not sure about the necessity of this line      
0

You do have a serious flaw in your non-recursive code.

After you check whether v is WHITE, you never mark it GRAY before pushing, so it may be seen as WHITE again and again from other unvisited nodes, resulting in multiple references to that v node pushed to the stack. This is potentially a catastrophic flaw (could cause infinite loops or such).

Also, you don't set .d except for root nodes. This means that the Nested set model attributes .ds and .fs won't be correct. (If you don't know what .ds and .fs are, have a read of that article, it was very enlightening to me back in the days. The article's left is your .d and right is your .f.)

Your inner if basically needs to be the same as the outer if minus the loops, plus the parent reference. That is:

11                  if v.color = WHITE
++                      v.color ← GRAY
++                      time ← time + 1
++                      v.d ← time
12                      v.π ← u
13                      push(v, S)

Correct that, and it should be a true equivalent.

Irfy
  • 9,323
  • 1
  • 45
  • 67
  • This happened when I copied my algorithm over from MS Word. I thought I had fixed everything, but I can be careless when proofreading sometimes. I edited the above to what I have. Sorry about that. I am hoping it is correct now. – Cory Gross Apr 26 '12 at 22:55
  • I added an image to the original post that clarifies how u.d and u.f will be assigned. – Cory Gross Apr 26 '12 at 23:07
  • Updated with very, very different info from original post. :) – Irfy Apr 27 '12 at 04:29
  • The `f` field of the parent will be set to the time right after pushing all the children, not after actually visiting them. The parent will wrap the start-times of its children, but not their whole subtrees. – Markus Jarderot Apr 27 '12 at 07:47
  • I just saw your update here when I got my popular question badge and I have corrected my original post, thanks so much. – Cory Gross Oct 31 '12 at 18:42
0

Here is the code in C++.

class Graph
{
    int V;                          // No. of vertices
    list<int> *adj;                 // Pointer to an array containing adjacency lists
public:
    Graph(int V);                    // Constructor
    void addEdge(int v, int w);             // function to add an edge to graph
    void BFS(int s);                    // prints BFS traversal from a given source s
    void DFS(int s);
};

Graph::Graph(int V)
{
    this->V = V;
    adj = new list<int>[V]; //list of V list
}

void Graph::addEdge(int v, int w)
{
    adj[v].push_back(w); // Add w to v’s list.
}


  void Graph::DFS(int s)
        {
                 //mark unvisited to each node
        bool *visited  = new bool[V];
        for(int i = 0; i<V; i++)
            visited[i] =false;
        int *d = new int[V];  //discovery
        int *f = new int[V]; //finish time

        int t = 0;       //time

        //now mark current node to visited
        visited[s] =true;
        d[s] = t;            //recored the discover time

        list<int> stack;

        list<int>::iterator i;

        stack.push_front(s);
        cout << s << " ";

        while(!(stack.empty()))
        {
            s= stack.front();
            i = adj[s].begin();

            while ( (visited[*i]) && (i != --adj[s].end()) )
            {
                ++i;
            }
            while ( (!visited[*i])  )
            {

                visited[*i] =true;
                t++;
                d[*i] =t;
                if (i != adj[s].end())
                    stack.push_front(*i);

                cout << *i << " ";
                i = adj[*i].begin();

            }

            s = stack.front();
            stack.pop_front();
            t++;
            f[s] =t;

        }
        cout<<endl<<"discovery time of the nodes"<<endl;

        for(int i = 0; i<V; i++)
        {
            cout<< i <<" ->"<< d[i] <<"    ";
        }
        cout<<endl<<"finish time of the nodes"<<endl;

        for(int i = 0; i<V; i++)
        {
            cout<< i <<" ->"<< f[i] <<"   ";
        }

    }

         int main()
         {
        // Create a graph given in the above diagram
        Graph g(5);
        g.addEdge(0, 1);
        g.addEdge(0, 4);
        g.addEdge(1, 4);
        g.addEdge(1, 2);
        g.addEdge(1, 3);
        g.addEdge(3, 4);
        g.addEdge(2, 3);


        cout << endl<<"Following is Depth First Traversal (starting from vertex 0) \n"<<endl;
        g.DFS(0);

        return 0;
    }

Simple iterative DFS. You can also see the discover time and finish time. Any doubts please comment. I have included sufficient comments to understand the code.

rusty
  • 652
  • 7
  • 21
-1

In the non-recursive version we need another color that reflects the state in the recursive stack. So, we will add a color=RED to indicate all children of the node were pushed to the stack. I will also assume the stack has a peek() method(which otherwise could be simulated with pop and immediate push)

So, with that addition the updated version of original post should look like:

for each vertex u ∈ G.V
      u.color ← WHITE
      u.π ← NIL
  time = 0
  for each vertex u ∈ G.V
      if u.color = WHITE
          u.color ← GRAY
          time ← time + 1
          u.d ← time
          push(u, S)
          while stack S not empty
              u ← peek(S)
              if u.color = RED
                  //means seeing this again, time to finish
                  u.color ← BLACK
                  time ← time + 1
                  u.f ← time
                  pop(S) //discard the result
              else
                  for each vertex v ∈ G.Adj[u]
                     if v.color = WHITE
                         v.color = GRAY
                         time ← time + 1
                         v.d ← time
                         v.π ← u
                         push(v, S)
                   u.color = RED
grdvnl
  • 636
  • 6
  • 9
-1
I used Adjacency Matrix:    

void DFS(int current){
        for(int i=1; i<N; i++) visit_table[i]=false;
        myStack.push(current);
        cout << current << "  ";
        while(!myStack.empty()){
            current = myStack.top();
            for(int i=0; i<N; i++){
                if(AdjMatrix[current][i] == 1){
                    if(visit_table[i] == false){ 
                        myStack.push(i);
                        visit_table[i] = true;
                        cout << i << "  ";
                    }
                    break;
                }
                else if(!myStack.empty())
                    myStack.pop();
            }
        }
    }
kmetin
  • 253
  • 2
  • 4
  • 14
-2

I believe that there is at least one case where the recursive and stack versions are not functionally equivalent. Consider the case of a Triangle - vertices A, B, and C connected to each other. Now, with the Recursive DFS, the predecessor graph that one would obtain with source A, would be either A->B->C OR A->C->B ( A->B implies A is the parent of B in depth first tree).

However, if you use the stack version of DFS, parents of both B and C, would always be recorded as A. It can never be the case that parent of B is C or vice-versa (which is always the case for recursive DFS). This is because, when exploring the adjacency list of any vertex (here A), we push all the members of adjacency list (here B and C) in one go.

This may become relevant, if you try to use DFS for finding articulation points in a graph[1]. One example would be that the following statement holds true only if we use the recursive version of DFS.

A root vertex is an articulation point if and only if it has at least two children in the depth first tree.

In a triangle, there is obviously no articulation point, but the stack-DFS still gives two children for any source vertex in the depth-first tree (A has children B and C). It's only if we create the depth first tree using recursive DFS that the above statement holds true.

[1] Introduction to Algorithms, CLRS - Problem 22-2 (Second and Third Edition)

gjain
  • 4,468
  • 5
  • 39
  • 47
  • Are you saying that by nature of the algorithms this is the difference between the stack and recursive implementations? Or are you saying that these differences between the two arise from my implementations and that they could be fixed to behave the same? – Cory Gross Feb 02 '13 at 18:35
  • My point is that the difference arises due to implementations. As I said, in the stack version we push all neighbors of a vertex in one go unlike the recursive case, where we _visit_ each neighbor node recursively. I have not gone through this link thoroughly, but it looks like it deals with the same issue. http://www.jroller.com/bobfoster/entry/depth_first_search_with_explicit – gjain Feb 02 '13 at 21:41