【问题标题】:Python Implement Breadth-First SearchPython 实现广度优先搜索
【发布时间】:2017-09-23 19:29:02
【问题描述】:

我在网上找到了一个例子,但是只返回BFS元素的序列是不够的。假设根是 BFS 树的第一级,然后它的子级是第二级,依此类推。从下面的代码中,我如何知道它们在哪个级别,以及每个节点的父级是谁(我将创建一个对象存储其父级和树级)?

# sample graph implemented as a dictionary
graph = {'A': ['B', 'C', 'E'],
         'B': ['A','D', 'E'],
         'C': ['A', 'F', 'G'],
         'D': ['B'],
         'E': ['A', 'B','D'],
         'F': ['C'],
         'G': ['C']}

# visits all the nodes of a graph (connected component) using BFS
def bfs_connected_component(graph, start):
   # keep track of all visited nodes
   explored = []
   # keep track of nodes to be checked
   queue = [start]

   # keep looping until there are nodes still to be checked
   while queue:
       # pop shallowest node (first node) from queue
       node = queue.pop(0)
       if node not in explored:
           # add node to list of checked nodes
           explored.append(node)
           neighbours = graph[node]

           # add neighbours of node to queue
           for neighbour in neighbours:
               queue.append(neighbour)
   return explored

bfs_connected_component(graph,'A') # returns ['A', 'B', 'C', 'E', 'D', 'F', 'G']

【问题讨论】:

  • 这篇文章属于codereview网站
  • @AmiNadimi 不幸的是,它不属于代码审查。它要求新的特征(主要是遍历中节点的前身)。要求新功能在 Code Review 上是题外话。请参阅our guide for Stack Overflow users 了解更多信息。
  • 理想的探索应该是一个 set(),否则这可能是 O(N*N)。 queue 也应该是一个 dequeue 以实现更快的 pop(0)

标签: python graph-theory breadth-first-search


【解决方案1】:

您可以通过首先将级别 0 分配给起始节点来跟踪每个节点的级别。然后为节点X的每个邻居分配级别level_of_X + 1

此外,您的代码会将同一个节点多次推送到队列中。我使用了一个单独的列表visited 来避免这种情况。

# sample graph implemented as a dictionary
graph = {'A': ['B', 'C', 'E'],
         'B': ['A','D', 'E'],
         'C': ['A', 'F', 'G'],
         'D': ['B'],
         'E': ['A', 'B','D'],
         'F': ['C'],
         'G': ['C']}


# visits all the nodes of a graph (connected component) using BFS
def bfs_connected_component(graph, start):
    # keep track of all visited nodes
    explored = []
    # keep track of nodes to be checked
    queue = [start]

    levels = {}         # this dict keeps track of levels
    levels[start]= 0    # depth of start node is 0

    visited= [start]     # to avoid inserting the same node twice into the queue

    # keep looping until there are nodes still to be checked
    while queue:
       # pop shallowest node (first node) from queue
        node = queue.pop(0)
        explored.append(node)
        neighbours = graph[node]

        # add neighbours of node to queue
        for neighbour in neighbours:
            if neighbour not in visited:
                queue.append(neighbour)
                visited.append(neighbour)

                levels[neighbour]= levels[node]+1
                # print(neighbour, ">>", levels[neighbour])

    print(levels)

    return explored

ans = bfs_connected_component(graph,'A') # returns ['A', 'B', 'C', 'E', 'D', 'F', 'G']
print(ans)

【讨论】:

  • 是否也可以找到他们的父母?
  • 我找到了他们的父母,非常感谢。但是最好使用deque,但是,我在图中的节点可能是整数,deque 不允许这样做。你知道如何解决这个问题吗?
  • pop(0) 删除数组的第一个元素。所以它被用作队列。
  • @user5575144:不确定deque 不能满足您的需求。它可以容纳任何物体。你不能索引它,但你不应该在这里索引queue。但是,queue 可以通过使用pop() 而不是pop(0) 与堆栈一样工作
  • 队列比递归调用有什么优势(如果可能,你能添加递归版本吗?)?
【解决方案2】:

是的,此代码仅以广度优先方式访问节点。对于许多应用程序来说,这本身就是一件有用的事情(例如在未加权的图中找到最短路径)

要真正返回 BFS 树需要一些额外的工作。您可以考虑为每个节点存储一个子节点列表,或者返回成对的(节点,父节点)。任何一种表示都应该允许您弄清楚树的结构。

这里要注意的另一件事是,代码使用 python 列表作为队列,这不是一个好主意。因为从列表中删除第一个元素需要 O(n) 时间。

【讨论】:

  • 但是,pop 正在被使用,所以它是 O(1)。它实际上是一个堆栈,而不是一个队列。
  • 当您将索引传递给pop 时,它会弹出该索引处的元素。在这种情况下,由于索引 0 被传递,它将删除列表的第一个元素。所以这个列表被一个队列使用了。
  • 糟糕!对:pop(0) 确实使它成为 q 队列,但是一个非常低效的队列,因为正如您所指出的那样,pop(0) 是 O(N)。最好使用deque,或者只使用列表作为堆栈(使用pop()),这在此处也可以正常工作。如果邻居一开始没有排序,则没有显着差异。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-06-02
  • 2013-05-20
  • 1970-01-01
  • 2011-01-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多