62

I have written a recursive DFS algorithm to traverse a graph:

void Graph<E, N>::DFS(Node n)
{
    std::cout << ReadNode(n) << " ";

    MarkVisited(n);

    NodeList adjnodes = Adjacent(n);

    NodeList::position pos = adjnodes.FirstPosition();

    while(!adjnodes.End(pos))
    {
        Node adj = adjnodes.ReadList(pos);

        if(!IsMarked(adj))
            DFS(adj);

        pos = adjnodes.NextPosition(pos);
    }
}

Then I have written an iterative DFS algorithm using a stack:

template <typename E, typename N>
void Graph<E, N>::IterativeDFS(Node n)
{
    Stack<Node> stack;

    stack.Push(n);

    while(!stack.IsEmpty())
    {
        Node u = stack.Read();

        stack.Pop();

        if(!IsMarked(u))
        {
            std::cout << ReadNode(u) << " ";

            MarkVisited(u);

            NodeList adjnodes = Adjacent(u);

            NodeList::position pos = adjnodes.FirstPosition();

            while(!adjnodes.End(pos))
            {
                stack.Push(adjnodes.ReadList(pos));

                pos = adjnodes.NextPosition(pos);
            }
        }
    }

My problem is that in a graph in which, for example, I enter the three nodes 'a', 'b', 'c' with arcs ('a', 'b') and ('a', 'c') my output is:

'a', 'b', 'c' with the recursive DFS version, and:

'a', 'c', 'b' with the iterative DFS one.

How could I get the same order? Am I doing something wrong?

Thank you!

dsolimano
  • 8,870
  • 3
  • 48
  • 63
JohnQ
  • 1,073
  • 2
  • 10
  • 17
  • 1
    Both orders are valid DFS orderings, since the nodes 'b' and 'c' are both reachable in one hop from the node 'a'. Are you worried that this is producing an invalid ordering? Or do you just want the two algorithms - both of which seem to produce valid orderings - to produce the same ordering? – templatetypedef Feb 08 '12 at 20:49
  • 1
    I know that using a stack I should be able to emulate a recursive function in an iterative way, so why don't I get the same output? – JohnQ Feb 08 '12 at 20:53
  • 1
    In iterative solution, when you push adjacency list of a vertex on stack, reverse the list before you push them onto stack. – Arjun Thakur Jun 13 '19 at 15:16

4 Answers4

109

Both are valid DFS algorithms. A DFS does not specify which node you see first. It is not important because the order between edges is not defined [remember: edges are a set usually]. The difference is due to the way you handle each node's children.

In the iterative approach: You first insert all the elements into the stack - and then handle the head of the stack [which is the last node inserted] - thus the first node you handle is the last child.

In the recursive approach: You handle each node when you see it. Thus the first node you handle is the first child.

To make the iterative DFS yield the same result as the recursive one - you need to add elements to the stack in reverse order [for each node, insert its last child first and its first child last]

shellter
  • 36,525
  • 7
  • 83
  • 90
amit
  • 175,853
  • 27
  • 231
  • 333
  • Oh, I understand. So it's about pushing the elements in the stack in the correct order. I have tried to read the list from the last element to the first one and now it works. Thank you! – JohnQ Feb 08 '12 at 21:02
  • @JohnQ: You are welcome. I'm glad it helped you, and I hope you understand not only *how* to fix these issue, but also why it happened and how to identify it in the future. – amit Feb 08 '12 at 21:08
  • Yes, of course. I have drawn some schemes to understand the situation :) – JohnQ Feb 08 '12 at 21:16
  • 1
    Doesn't OP's code end up being a breadth-first search? Depth-first should go all the way down the first path first, non? – Claudiu Feb 05 '16 at 18:14
  • 2
    @Claudiu No, the code does go depth first, the only change in the order of exploring nodes is the order the siblings are visited. This is not contradicting correctness of DFS, since nodes and edges are usually considered *sets*, which is unordered by definition – amit Feb 05 '16 at 21:40
  • @amit: Ah yes, I didn't recognize that a stack was being used, and so the order would indeed be DFS. Whereas if it were a queue then the same algorithm would be BFS. I was thinking DFS had to have the same order, which is still possible to do iteratively, but it's a bit more complicated. – Claudiu Feb 05 '16 at 22:42
  • @Claudiu The answer explains how to maintain the same order (push elements to the stack in reverse iteration order, so if your ordered list of out going edges from some vertex `v` is `(v,u1),(v,u2),...,(v,uk)`, push `uk` first and `u1` last. – amit Feb 10 '16 at 18:13
  • @amit: Oh yes, doubly silly me... thanks for twice pointing out that which was already written! – Claudiu Feb 10 '16 at 19:51
  • @amit Could you use an extra stack to yield the same results between the iterative DFS and recursive DFS? – RoadRunner Mar 19 '17 at 04:13
  • @RoadRunner I guess so, but you don't need an extra stack to do so, I already suggested a simpler solution to get the same results. – amit Mar 19 '17 at 11:23
4

The most obvious diff is the order you utilize the children.

In the Recursive method: You take the first child and run with it as soon as it comes

while in iterative approach: You push all the children in the stack and then take the top of the stack i.e the last child

To produce the same result just do the insertion of children in reverse order.

The other diff would be memory usage as one would use call stack while the other would use a stack that you make or one the STL elements:

You can read about this here: https://codeforces.com/blog/entry/17307

ASHUTOSH SINGH
  • 131
  • 1
  • 13
2

Here I leave my solution recursively, very fast to implement. It is only a matter of adjusting it for any problem that requires the use of this algorithm.

It is very important to mark the current state as visited, defined as ok[u] = true, even having all the states as they have not been visited using memset(ok, 0, sizeof ok)

#define forn(i , a , b) for(int i=(a);i<(b);i++)

vector<int> arr[10001];
bool ok[10001];

void addE(int u , int v){
  arr[u].push_back(v);
  arr[v].push_back(u);
}

void dfs(int u){
  ok[u] = true;
  forn(v , 0 , (int)arr[u].size()) if(!ok[arr[u][v]]) dfs(arr[u][v]);
}

int main(){
  //...
  memset(ok , 0 , sizeof ok);
  //... 
  return 0;
}
Chris Michael
  • 319
  • 6
  • 18
-1

Below is the sample code (as per @amit answer above) in C# for Adjacency Matrix.

using System;
using System.Collections.Generic;

namespace GraphAdjMatrixDemo
{
    public class Program
    {
        public static void Main(string[] args)
        {
            // 0  1  2  3  4  5  6
            int[,] matrix = {     {0, 1, 1, 0, 0, 0, 0},
                                  {1, 0, 0, 1, 1, 1, 0},
                                  {1, 0, 0, 0, 0, 0, 1},
                                  {0, 1, 0, 0, 0, 0, 1},
                                  {0, 1, 0, 0, 0, 0, 1},
                                  {0, 1, 0, 0, 0, 0 ,0},
                                  {0, 0, 1, 1, 1, 0, 0}  };

            bool[] visitMatrix = new bool[matrix.GetLength(0)];
            Program ghDemo = new Program();

            for (int lpRCnt = 0; lpRCnt < matrix.GetLength(0); lpRCnt++)
            {
                for (int lpCCnt = 0; lpCCnt < matrix.GetLength(1); lpCCnt++)
                {
                    Console.Write(string.Format(" {0}  ", matrix[lpRCnt, lpCCnt]));
                }
                Console.WriteLine();
            }

            Console.Write("\nDFS Recursive : ");
            ghDemo.DftRecursive(matrix, visitMatrix, 0);
            Console.Write("\nDFS Iterative : ");
            ghDemo.DftIterative(matrix, 0);

            Console.Read();
        }

        //====================================================================================================================================

        public void DftRecursive(int[,] srcMatrix, bool[] visitMatrix, int vertex)
        {
            visitMatrix[vertex] = true;
            Console.Write(vertex + "  ");

            for (int neighbour = 0; neighbour < srcMatrix.GetLength(0); neighbour++)
            {
                if (visitMatrix[neighbour] == false && srcMatrix[vertex, neighbour] == 1)
                {
                    DftRecursive(srcMatrix, visitMatrix, neighbour);
                }
            }
        }

        public void DftIterative(int[,] srcMatrix, int srcVertex)
        {
            bool[] visited = new bool[srcMatrix.GetLength(0)];

            Stack<int> vertexStack = new Stack<int>();
            vertexStack.Push(srcVertex);

            while (vertexStack.Count > 0)
            {
                int vertex = vertexStack.Pop();

                if (visited[vertex])
                    continue;

                Console.Write(vertex + "  ");
                visited[vertex] = true;

                for (int neighbour = srcMatrix.GetLength(0) - 1; neighbour >= 0; neighbour--)
                //for (int neighbour = 0; neighbour < srcMatrix.GetLength(0); neighbour++)
                {
                    if (srcMatrix[vertex, neighbour] == 1 && visited[neighbour] == false)
                    {
                        vertexStack.Push(neighbour);
                    }
                }
            }
        }
    }
}
Sai
  • 1,376
  • 2
  • 15
  • 25
  • 1
    Your code does it wrongly. Note that my answers specified you only need to **PUSH** the elements in reverse order, while in fact you do 2 more things in your code: (1) Change the place where "visited" is checked. (2) Change the place where element is printed (in your code: when it is inserted to the list, and not when it is popped from it). I took your code and fixed the above two flows, it is available in [ideone](https://ideone.com/OW7JhS), and is working properly. Hope I did not miss any other issue. – amit Mar 19 '17 at 10:29
  • To elaborate: Regarding (1): The recursive call sets `visited` upon exploring the node, and the iterative code upon inserting the node. Regarding (2): The print in recursive call is when node is explored (popped from stack), and in iterative when it is pushed to stack. Those two difference will lead to different results if you try two recursive solutions with one with each variant of the above caveats. – amit Mar 19 '17 at 11:17
  • Thank you for the quick reply and correcting me @amit. I almost did the same as you mentioned (Missed to place the latest) and the logic I was missing in my code is if (visited[vertex] == true) continue; I up voted your answer and comment :) and updated my answer for matrix example for others. – Sai Mar 19 '17 at 17:21