深度优先搜索实施中的输出不正确

时间:2017-11-15 18:39:59

标签: python-3.x graph depth-first-search

最近,我在网站(https://brilliant.org/wiki/depth-first-search-dfs/)中查看了深度优先搜索的代码。但是它们的实现并不完全正确。这是他们发布的代码

def depth_first_search(graph):
    visited, stack = set(), [root]
    while stack:
        vertex = stack.pop()
        if vertex not in visited:
            visited.add(vertex)
            stack.extend(graph[vertex] - visited)
    return visited

正如您所看到的,他们应用的逻辑是正确的,但他们使用set操作,每次程序运行时都会更改输出。

这是我的完整程序

graph = {'A': {'B', 'S'}, 'B': {'A'}, 'C': {'S', 'F', 'D', 'E'},
     'D': {'C'}, 'E': {'H', 'C'}, 'F': {'C', 'G'}, 'G': {'S', 'F', 'H'}, 
     'H': {'G', 'E'}, 'S': {'A', 'G', 'C'}}

def depth_first_search(graph, root):
    visited, stack = set(), [root]
    while stack:
        vertex = stack.pop()
        if vertex not in visited:
            visited.add(vertex)
            stack.extend(graph[vertex] - visited)
    return visited

print(depth_first_search(graph, 'A'))

以下是我得到的输出我运行程序

{'H', 'C', 'B', 'A', 'D', 'S', 'F', 'E', 'G'}
{'D', 'E', 'C', 'H', 'G', 'S', 'A', 'B', 'F'}
{'G', 'F', 'C', 'H', 'E', 'D', 'B', 'S', 'A'} and so on....

使用set的原因尤其适用于下面的代码行,因为我们希望堆栈只存储未探测的顶点。

stack.extend(graph[vertex] - visited)

因此,执行设置差异操作实现了这个目标。但是它的成本如上所述。所以我稍微调整了代码以避免使用set和make list

graph = {'A': ['B', 'S'], 'B': ['A'], 'C': ['S', 'F', 'D', 'E'],
 'D': ['C'], 'E': ['H', 'C'], 'F': ['C', 'G'], 'G': ['S', 'F', 'H'],
  'H': ['G', 'E'], 'S': ['A', 'G', 'C']}

def depth_first_search(graph, root):
    visited, stack = [], [root]
    while stack:
        vertex = stack.pop()
        if vertex not in visited:
            visited.append(vertex)
            neighbours = graph[vertex]
            for neighbour in neighbours:
                # ensures we only add unexplored nodes to the stack
                if neighbour not in visited:
                    stack.append(neighbour)
    return visited

print(depth_first_search(graph, 'A'))

但是我得到了错误的结果

['A', 'S', 'C', 'E', 'H', 'G', 'F', 'D', 'B']

正确的结果必须是

['A', 'B', 'S', 'C', 'D', 'E', 'H', 'G', 'F']

我做错了什么?

2 个答案:

答案 0 :(得分:2)

您获得的答案是该图表的有效DFS订单。似乎存在一个不成文的限制,即必须按字母顺序遍历节点的邻居。考虑到这一点,有两件事:

首先,您将按照定义的顺序将节点的邻居添加到堆栈。但是当您pop()离开堆栈时,首先将 last 项目从堆栈中取出。这意味着您以相反的顺序选择节点。通过颠倒迭代邻居的顺序,这很容易解决:

for neighbour in reversed(neighbours):

其次,您实际上并没有按字母顺序在graph中定义节点的邻居。您需要在定义中按字母顺序排列graph的值,或者在迭代之前对它们进行排序:

for neighbour in reversed(sorted(neighbours)):
# or
for neighbour in sorted(neighbours, reverse=True):

答案 1 :(得分:1)

看起来您拥有的初始代码示例并非旨在生成拓扑排序,而只是列出可从根目录到达的所有节点。可能很重要的是要注意它不是不正确的,它不应该给你订单。

您的代码基本上按照它应该的方式执行,并且您获得的输出与您期望的输出一样正确。只要你想要一个DFS即可。

我认为你缺少的是,当你致电vertex = stack.pop()时,你会忘记它总是返回最后一个(即最右边的元素),当你打电话给stack.append(neighbour)时,你'按照从左到右的顺序将孩子们推到堆叠上。

如果你想要一个DFS首先专门关闭“最左边”分支,那么你需要在将它们添加到堆栈之前颠倒邻居/子节点的顺序。

编辑:我还没有足够的代表自由评论,但我的答案与glibdud的答案基本相同。您遇到的问题似乎是您在头脑中应用了实际上不属于基本DFS的其他限制。