使用BFS进行拓扑排序

时间:2014-08-10 14:35:32

标签: algorithm breadth-first-search

可以使用宽度优先搜索来查找图中顶点和强连通分量的拓扑排序吗?

如果是,该怎么做?如果不是为什么不呢?

我们通常在这些问题中使用深度优先搜索但是如果我尝试使用BFS实现会有什么问题呢?

这样的代码会起作用吗?

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

3 个答案:

答案 0 :(得分:6)

是的,您可以使用 BFS 进行拓扑排序。实际上我记得有一次我的老师告诉我,如果问题可以通过 BFS 来解决,那么永远不要选择通过 DFS 来解决。由于 BFS 的逻辑比 DFS 更简单,因此大多数时候您总是希望直接解决问题。

您需要从 indegree 0 的节点开始,这意味着没有其他节点指向它们。请务必先将这些节点添加到结果中。您可以使用HashMap映射每个节点的indegree,以及一个在BFS中常见的队列,以帮助您进行遍历。当您从队列中轮询节点时,其邻居的indegree需要减少1,这就像从图中删除节点并删除节点与其邻居之间的边缘。每次遇到具有0 indegree的节点时,将它们提供给队列以便稍后检查它们的邻居并将它们添加到结果中。

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;
}

答案 1 :(得分:4)

他们有相似名称的事实并没有使他们成为类似的方法。

DFS通常使用LIFO(如果你愿意,可以使用堆栈)实现 - 先进先出。

BFS通常用FIFO实现(如果你愿意,可以用队列) - 先进先出。

您可以以任何您想要的方式遍历图形,并最终得出其节点的拓扑顺序。但是如果你想有效地做到这一点,那么DFS是最好的选择,因为节点的拓扑顺序基本上反映了它们在图中的深度(好吧,&#34;依赖深度&#34;更准确)。 / p>

答案 2 :(得分:1)

因此,通常,使用DFS(深度优先搜索)进行拓扑排序的代码要直接得多,您可以运行它并回溯,因为它在递归分配数字时会回溯到先前的堆栈帧。 BFS不太直接,但仍然易于理解。

首先,必须计算图形上所有顶点的度数,这是因为必须从度数为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]++;
        }
      }

因此,上面的代码循环遍历顶点数组,然后循环遍历单个顶点的边缘(在本例中是使用链表存储的),然后递增该边缘指向indeg数组的顶点。因此,在外循环结束时,您将遍历每个顶点的邻居并计算每个顶点的入度。

第二,现在您必须使用BFS对该图进行实际的拓扑排序。因此,这第一段代码只会将入度为0的图中的顶点排入队列。

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

现在,仅将入度为0的顶点排入队列后,就可以开始循环以分配拓扑数字。

    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);
        }
      }

因此,print语句将打印出与该顶点对应的顶点号。因此,根据程序的要求,您可以将print语句所在的代码更改为存储顶点编号或名称或沿这些行的内容的代码。除此之外,我希望这有助于回答第一个问题。

第二个问题

对于问题的第二部分,这很简单。

1。创建填充有错误值的布尔数组,这将表示是否已访问顶点。

2.Create for循环遍历adjList数组,在此循环内,您将调用bfs,在调用bfs之后,您将遍历所创建的布尔数组,检查是否有任何值是false,如果是,则该图不是牢固连接,您可以返回“图形未牢固连接”并结束程序。在外部for循环的每次迭代结束时(但在内部for循环之后),请不要忘记将布尔数组重新设置为所有假值。

3。至此,外部for循环完成,您可以返回true,实现bfs是您的工作,它应该接受一个整数,并将您创建的已访问布尔数组作为参数。