简体   繁体   English

当存在循环时,是否可以使用 DFS 遍历图中的所有连接节点?

[英]Is it possible to traverse all connected nodes in a graph with DFS when cycles are present?

Is it possible to traverse all connected nodes in a graph with DFS when cycles are present?当存在循环时,是否可以使用 DFS 遍历图中的所有连接节点?

g = {'a':['b','c'],
     'b':['a','f'],
     'c':['a','f'],
     'd':['c','e'],
     'e':['d'],
     'f':['c','b'],
     }

def dfs(graph, node):
  stack = [node]
  visited = []
  while stack:
    current = stack.pop()
    visited.append(current)
    next_nodes = list(filter(lambda x: x not in visited, graph[current]))
    stack.extend(next_nodes)
  return visited

dfs(g,'a')   
>>>
['a', 'c', 'f', 'b', 'b']

My solution is unable to reach d or e .我的解决方案无法达到de Also it visits b twice, which is bizarre.它还访问b两次,这很奇怪。 How could this code be altered to traverse all nodes (if possible) without repeats to the visited array?如何更改此代码以遍历所有节点(如果可能)而不重复visited的数组?

You need to check whether a given node is already on the stack, else you may end up processing the same node twice:您需要检查给定节点是否已经在堆栈上,否则您可能最终会处理同一节点两次:

def dfs(graph, node):
    stack = [node]
    visited = []
    while stack:
        current = stack.pop()
        visited.append(current)
        next_nodes = list(filter(lambda x: x not in visited + stack, graph[current]))
        stack.extend(next_nodes)
    return visited

As for the issue of some nodes not being visited, none of the nodes reachable from 'a' have any outgoing neighbors to 'd' or 'e' .至于某些节点未被访问的问题,从'a'可到达的节点都没有到'd''e'任何传出邻居。 If your graph is meant to be undirected, you need to make sure that you're adding all the right entries for each node.如果您的图表是无向的,您需要确保为每个节点添加了所有正确的条目。 If your graph is meant to be directed, this is expected behavior.如果您的图表是定向的,这是预期的行为。


We can also optimize this code.我们也可以优化这段代码。 You could maintain a separate seen set, to be able to check more quickly whether you've seen a node or not (seen == "on the stack or already visited"):seen维护一个单独的已查看集,以便能够更快地检查您是否已查看节点(已查看 ==“在堆栈上或已访问”):

def dfs(graph, node):
    stack = [node]
    seen = {node}
    visited = []
    while stack:
        current = stack.pop()
        visited.append(current)
        next_nodes = list(filter(lambda x: x not in seen, graph[current]))
        stack.extend(next_nodes)
        seen.update(next_nodes)

    return visited

This outputs:这输出:

['a', 'c', 'f', 'b']

You check whether a node was visited or not when putting it on the stack, but only mark it visited when it is popped from the stack.您在将节点放入堆栈时检查它是否被访问,但仅在从堆栈中弹出时标记它已访问。 This means any nodes that are on the stack are allowed to be pushed to the stack again (since they are not yet marked as visited at that point).这意味着堆栈上的任何节点都可以再次被推入堆栈(因为此时它们还没有被标记为已访问)。

You need to either change visited to seen or something like that, to note that they have been added to the stack, and add the next_nodes to it instead of adding the node at visit time, or change the way next_nodes is generated in order to only take nodes that are neither in visited nor stack .您需要将visited更改为seen或类似的东西,以注意它们已添加到堆栈中,并将next_nodes添加到其中而不是在访问时添加节点,或者更改生成next_nodes的方式以便仅获取既不在visited也不在stack中的节点。


Unrelatedly, I'd make visited a set , not a list , for performance reasons.不相关的是,出于性能原因,我会visited set ,而不是list

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM