在图中实现(修改的)DFS

时间:2017-04-05 09:33:01

标签: python algorithm graph

我在Python中实现了一个简单的图形数据结构,结构如下。这里的代码只是为了澄清函数/变量的含义,但它们非常明显,因此您可以跳过阅读。

class Node: 

    def __init__(self, label):   
        self.out_edges = []
        self.label = label
        self.is_goal = False
        self.is_visited = False


    def add_edge(self, node, weight):              
        self.out_edges.append(Edge(node, weight))

    def visit(self):                              
        self.is_visited = True


class Edge:

    def __init__(self, node, weight):              
        self.node = node
        self.weight = weight


    def to(self):                                  
        return self.node


class Graph:    

    def __init__(self): 
        self.nodes = []


    def add_node(self, label):                    
        self.nodes.append(Node(label))


    def visit_nodes(self):                                                 
        for node in self.nodes:
            node.is_visited = True

现在我正在尝试实现从给定节点v开始的深度优先搜索,并将路径(以列表形式)返回到目标节点。按目标节点,我的意思是属性is_goal设置为true的节点。如果存在路径,并且找到目标节点,则将字符串':-)'添加到列表中。否则,该函数只执行DFS并尽可能地去。 (我这样做只是为了轻松检查路径是否存在)。

这是我的实施:

def dfs(G, v):
    path = []                                   # path is empty so far

    v.visit()                                   # mark the node as visited
    path.append(v.label)                        # add to path

    if v.is_goal:                               # if v is a goal node
        path.append(':-)')                      # indicate a path is reached
        G.visit_nodes()                         # set all remaining nodes to visited

    else:
        for edge in v.out_edges:                # for each out_edge of the starting node
            if not edge.to().is_visited:        # if the node pointed to is not visited
                path += dfs(G, edge.to())       # return the path + dfs starting from that node

    return path

现在问题是,我必须设置所有要访问的节点(第9行,visit_nodes()),以便在达到目标节点时结束算法。实际上,这种打破等待等待的递归调用,因为它确保没有其他节点添加到路径中。我的问题是:

  

有更干净/更好的方法吗?

解决方案似乎有些麻烦。我很感激任何帮助。

1 个答案:

答案 0 :(得分:1)

最好不要使访问信息使图结构混乱,因为这实际上是与搜索算法相关联的上下文敏感信息,而不是图本身。您可以改为使用单独的一组。

其次,您在代码中有一个错误,因为您继续添加path变量,即使您的递归调用未找到目标节点。所以你的路径甚至会有顺序的节点,它们之间没有边缘,但是(近或远)兄弟姐妹/堂兄弟。

相反,您应该只在找到目标节点时返回路径,然后在进行递归调用之后,您应该测试该条件以确定是否使用您正在尝试的当前边缘节点作为该路径的前缀。

实际上不需要保留路径变量,因为根据递归级别,您只需要将一个节点添加到从递归调用获得的路径中。没有必要将该节点存储在列表中。只需要一个简单的变量即可。

以下是建议的代码(未经测试):

def dfs(G, v):
    visited = set() # keep visited information away from graph

    def _dfs(v):
        visited.add(v) # mark the node as visited
        if v.is_goal:
            return [':-)'] # return end point of path
        for edge in v.out_edges:
            neighbor = edge.to() # to avoid calling to() several times
            if neighbor not in visited:
                result = _dfs(neighbor)
                if result: # only when successful
                    # we only need 1 success: add current neighbor and exit
                    return [neighbor.label] + result 
                # otherwise, nothing should change to any path: continue

        # don't return anything in case of failure

    # call nested function: the visited and Graph variables are shared
    return _dfs(v) 

备注

出于与visited相同的原因,最好也从图中删除is_goal标记,并将该目标节点作为附加参数传递给dfs功能

weight参数提供默认值也很好,这样您也可以将此代码用于未加权的图形。

了解它如何在repl.it上有5个节点的示例图上运行。