【问题标题】:Topological search and Breadth first search拓扑搜索和广度优先搜索
【发布时间】:2013-02-16 03:16:38
【问题描述】:

是否可以使用广度优先搜索逻辑来进行 DAG 的拓扑排序? Cormen 中的解决方案使用了深度优先搜索,但使用 BFS 会不会更容易?

原因: BFS 在访问具有下一个深度值的节点之前访问特定深度中的所有节点。这自然意味着,如果我们做 BFS,父母会排在孩子之前。这不正是我们对拓扑排序所需要的吗?

【问题讨论】:

标签: algorithm graph


【解决方案1】:

对于一棵树(或森林)来说,仅仅 BFS 就足够了,因为在树(森林)中,入度最多为 1。 现在,看看这个案例:

B → C → D
      ↗
    A

队列初始化为A B(入度为零)的BFS 将返回A B D C,它不是拓扑排序的。这就是为什么您必须保持入度计数,并且只选择计数已降至零的节点。 (*)

顺便说一句,这是您的“原因”的缺陷:BFS 仅保证之前曾拜访过一位父母,而不是全部。

编辑:(*) 换句话说,您将入度为零的相邻节点推回(在示例中,处理完AD 将被跳过)。因此,您仍在使用队列,并且刚刚在通用算法中添加了过滤步骤。话虽如此,继续称它为 BFS 是有问题的。

【讨论】:

    【解决方案2】:

    这是可能的,甚至是维基百科describes an algorithm based on BFS

    基本上,您使用一个队列,在其中插入所有没有传入边的节点。然后,当您提取一个节点时,您会删除它的所有出边并插入可以从它到达但没有其他入边的节点。

    【讨论】:

      【解决方案3】:

      在 BFS 中,您实际行走的所有边缘最终都会朝着正确的方向。但是,如果您按 BFS 顺序布置图,所有您不走的边(那些在相同深度的节点之间,或者从更深的节点回到更早的节点)最终都会走错路。

      是的,您确实需要 DFS 来做到这一点。

      【解决方案4】:

      是的,您可以使用 BFS 进行拓扑排序。其实我记得有一次我的老师告诉我,如果问题可以通过BFS解决,千万不要选择DFS来解决。因为 BFS 的逻辑比 DFS 更简单,所以大多数时候您总是想要一个简单的问题解决方案。

      正如 YvesgereY 和 IVlad 所提到的,您需要从 indegree0 的节点开始,这意味着没有其他节点直接指向它们。请务必先将这些节点添加到您的结果中。您可以使用 HashMap 来映射每个节点及其入度,并使用 BFS 中非常常见的队列来帮助您遍历。当您从队列中轮询一个节点时,其邻居的入度需要减少 1,这就像从图中删除该节点并删除该节点与其邻居之间的边一样。每次遇到度数为 0 的节点时,将它们提供给队列以便稍后检查其邻居并将它们添加到结果中。

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

      【讨论】:

        猜你喜欢
        • 2011-01-31
        • 2016-02-16
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-06-02
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多