20

Can Breadth first Search be used for finding topological sorting of vertices and strongly connected components in a graph?

If yes how to do that? and If not why not?

we generally use Depth first search in these problems but What will be the problem if I try to implement using BFS?

Will code like this work?

def top_bfs(start_node):
    queue = [start_node]
    stack = []
    while not queue.empty():
        node = queue.dequeue()
        if not node.visited:
            node.visited = True
            stack.push(node)
            for c in node.children:
                queue.enqueue(c) 
    stack.reverse()
    return stack
monkey
  • 561
  • 1
  • 7
  • 16
  • 1
    Possible duplicate of [Topological search and Breadth first search](http://stackoverflow.com/questions/14906533/topological-search-and-breadth-first-search) – gengkev Dec 28 '15 at 19:02
  • 1
    actually, there are many duplicate questions: http://stackoverflow.com/q/12373495/689161 http://stackoverflow.com/q/30869987/689161 – gengkev Dec 28 '15 at 19:29
  • 1
    Basically, there are two algorithms used for topological sort, described on [Wikipedia](https://en.wikipedia.org/wiki/Topological_sorting). Kahn's algorithm works with any graph traversal, including BFS or DFS. Tarjan's algorithm, which is now the most well known, uses a "reverse DFS postorder" traversal of the graph. Postorder means you add a node to the list after visiting its children. Since you don't keep track of this when you conduct BFS (instead, you just append to a queue), you need to use DFS for Tarjan's algorithm. – gengkev Dec 28 '15 at 19:37

3 Answers3

25

Yes, you can do topological sorting using BFS. Actually I remembered once my teacher told me that if the problem can be solved by BFS, never choose to solve it by DFS. Because the logic for BFS is simpler than DFS, most of the time you will always want a straightforward solution to a problem.

You need to start with nodes of which the indegree is 0, meaning no other nodes direct to them. Be sure to add these nodes to your result first.You can use a HashMap to map every node with its indegree, and a queue which is very commonly seen in BFS to assist your traversal. When you poll a node from the queue, the indegree of its neighbors need to be decreased by 1, this is like delete the node from the graph and delete the edge between the node and its neighbors. Every time you come across nodes with 0 indegree, offer them to the queue for checking their neighbors later and add them to the result.

public ArrayList<DirectedGraphNode> topSort(ArrayList<DirectedGraphNode> graph) {

  ArrayList<DirectedGraphNode> result = new ArrayList<>();
    if (graph == null || graph.size() == 0) {
      return result;
    }
  Map<DirectedGraphNode, Integer> indegree = new HashMap<DirectedGraphNode, Integer>();
  Queue<DirectedGraphNode> queue = new LinkedList<DirectedGraphNode>();

//mapping node to its indegree to the HashMap, however these nodes
//have to be directed to by one other node, nodes whose indegree == 0
//would not be mapped.
  for (DirectedGraphNode DAGNode : graph){
      for (DirectedGraphNode nei : DAGNode.neighbors){
          if(indegree.containsKey(nei)){
              indegree.put(nei, indegree.get(nei) + 1);
          } else {
              indegree.put(nei, 1);
          }
      }
  }


//find all nodes with indegree == 0. They should be at starting positon in the result
  for (DirectedGraphNode GraphNode : graph) {
      if (!indegree.containsKey(GraphNode)){
          queue.offer(GraphNode);
          result.add(GraphNode);
      }
  }


//everytime we poll out a node from the queue, it means we delete it from the 
//graph, we will minus its neighbors indegree by one, this is the same meaning 
//as we delete the edge from the node to its neighbors.
  while (!queue.isEmpty()) {
      DirectedGraphNode temp = queue.poll();
      for (DirectedGraphNode neighbor : temp.neighbors){
          indegree.put(neighbor, indegree.get(neighbor) - 1);
          if (indegree.get(neighbor) == 0){
              result.add(neighbor);
              queue.offer(neighbor);
          }
      }
  }
  return result;
}
Caiwei Wu
  • 361
  • 1
  • 3
  • 6
  • 2
    This should be the accepted answer IMO. The only thing I might add (if it it's not given that the graph is a DAG) is to check if any remaining node's indegree isn't zero once the queue is empty. In that case, the graph contains a cycle. – akurtser May 16 '21 at 18:29
  • `Actually I remembered once my teacher told me that if the problem can be solved by BFS, never choose to solve it by DFS. Because the logic for BFS is simpler than DFS`: it depends on the problem, on sparseness or denseness of the graph, or bushiness of the tree, diameter of the graph or hieght of the tree, etc. Not something we can say in general. In fact, for many problems DFS is the more natural choice due to its lower space complexity compared to BFS. – Nader Ghanbari Mar 14 '22 at 04:56
13

The fact that they have similar names doesn't make them similar methods.

DFS is typically implemented with LIFO (a stack if you will) - last in first out.

BFS typically implemented with FIFO (a queue if you will) - first in first out.

You can walk a graph in any way you want, and eventually come out with a topological order of its nodes. But if you want to do it efficiently, then DFS is the best option, as the topological order of the nodes essentially reflects their depth in the graph (well, "dependency-depth" to be more accurate).

barak manos
  • 29,648
  • 10
  • 62
  • 114
  • So there is definitely some( though inefficient than using DFS)way that i can modify BFS to find topological ordering of nodes? – monkey Aug 10 '14 at 14:58
  • @monkey: Well you can walk the nodes `O(n^5)` times (breadth-wise if you want), put some "side notes" for every node (like, a list of it's immediate successors or whatever), and eventually get the topological order. The answer to your question depends on your "willingness" to call BFS a "BFS" after changing its basic behavior to the extent of affecting its complexity. I don't think I'd call it "BFS" by then. So the question is kind of irrelevant, because once you modify BFS it is no longer BFS, right? – barak manos Aug 10 '14 at 15:38
  • 1
    @monkey: It looks like it might work, but personally, once I see a `stack` I no longer consider it as `BFS`. – barak manos Aug 11 '14 at 07:21
  • Yes it is not exact BFS but still nodes are traversed in BFS order only. – monkey Aug 11 '14 at 07:30
  • @monkey: Well, as you can probably understand from the internal `for`, the complexity of your algorithm is not ideal... But I guess that it answers your purpose as described in the question... – barak manos Aug 11 '14 at 07:33
  • @monkey there are some very helpful BFS approaches discussed in [this](https://www.quora.com/Can-topological-sorting-be-done-using-BFS) quora post. – RBT Jul 13 '18 at 00:06
  • Can you explain more why BFS is less efficient than DFS for topological sort? – cxs1031 Jul 11 '21 at 05:10
  • 2
    This answer is factually wrong as DFS is not "more efficient". In fact when possible, BFS is better than DFS, as it doesn't use recursion. This also doesn't answer the question and is overly vague. – mbrt Jan 27 '22 at 17:26
1

So generally the code for topologically sorting using DFS (depth first search) is much more straight forward, you run it and it backtracks since its recursive assigning numbers as it calls back to previous stack frames. BFS is less straight forward but still easy to understand.

First, you must calculate the in-degree of all the vertices on the graph, this is because you must start at a vertex that has an in-degree of 0.

    int[] indegree = int[adjList.length];
    for(int i = 0; i < adjList.length; i++){
        for(Edge e = adjList[i]; e != null; e = e.next){
          indegree[e.vertexNum]++;
        }
      }

So the code above iterates through the vertex array, then it iterates through a single vertex's edges(in this case its stored using linked list), then it increments the vertex that the edge is pointing to in the indegree array. So at the end of the outer loop you will have traversed each vertex's neighbors and calculated each vertex's in-degree.

Second, you now must use BFS to actually topologically sort this graph. So this first snippet of code will only enqueue the vertices in the graph that have an in-degree of 0.

    Queue<Integer> q = new Queue<>();
    for(int i = 0; i < indegree.length; i++){
      if(indegree[i] == 0){
        q.enqueue(i);
      }
    }

Now, after enqueueing only vertices with in-degree of 0, you start the loop to assign topological numbers.

    while(!q.isEmpty()){
      int vertex = q.dequeue();
      System.out.print(vertex);
      for(Edge e = adjList[vertex]; e != null; e = e.next){
        if(--indegree[e.vnum] == 0){
          q.enqueue(e.vnum);
        }
      }

So the print statement prints out the vertex number that corresponds to the vertex. So depending on the requirements of your program, you can change the code where the print statement is to something that stores the vertex numbers or the names or something along those lines. Other than that, I hope this helped answer the first question.

Second Question

As for the second part of the question, it's pretty simple.

1.Create boolean array filled with false values, this will represent if the vertices have been visited or not.

2.Create for loop iterating over the adjList array, inside this loop you will call bfs, after calling bfs you will iterate over the boolean array you created, checking if any value is false, if it is then the graph is not strongly connected and you can return "graph is not strongly connected" and end the program. At the end of each iteration of the outer for-loop (but after the inner for-loop) don't forget to reset your boolean array to all false values again.

3.At this point the outer for loop is done and you can return true, it is your job to implement to bfs it should take in an integer and the visited boolean array you created as parameters.

Jade Olayy
  • 59
  • 5