【问题标题】:Finding a cycle in a DFS directed graph在 DFS 有向图中找到一个循环
【发布时间】:2016-01-30 01:11:01
【问题描述】:

我正在尝试使用 dfs 搜索在有向图中找到一个循环。我正在从包含顶点所有邻居的文本文件中读取文件。每当我调用 cycle_exists 方法时,我都会得到全部错误,所以答案永远不会改变。

Vertex.py 
"""
__version__ = 'October 2015'

Vertex class reads in our graph and
performs a depth first search on it
and performs the transitive closure operation.
Vertex class also checks for cycles in our graph.
"""


import sys
class Graph:

    def __init__(self):
        """
        Initialize the variable used in Graph
        """
        self.dfsPaths = [] #list for dfsPaths
        self.VertexList = {} #list for adjacent vertices


    def readInputGraph(self, inputFile):
        """
        Reads specified input file and stores in
        adjacency list
        :param inputFile: file to be rad in
        :return: the VertexList
        """
        file = open(inputFile, 'r') #open the file and read it
        for line in file: #for each element in the file
            (vertex,val) = line.split() #vertex gets first value in the line, val gets second
            if vertex not in self.VertexList: #if vertex not in VertexList
               self.VertexList[vertex] = set([val]) #add adjacent pairs
            else:   #else
                self.VertexList.get(vertex).add(val) #add the values


        for i in list(self.VertexList.keys()): #for each element in the list of the vertex keys
            for j in self.VertexList[i]: # for each vertex that's in i
                if j not in self.VertexList: #if j is not in the vertex list
                    self.VertexList[j] = set() #we add it to the vertex list

        return self.VertexList #return list of adjacent vertices

    def dfsSearch(self, graph, start, end, path = []):
        """
        Performs a depth first search on
        the graph that is read in from the file
        :param graph: the graph that we are performing the search on
        :param start: the starting vertex
        :param end: the target vertex
        :param path: a list of the paths
        :return: the paths from the search
        """
        path = path + [start] #path
        if start == end: #if the start element and end element are the same
            return [path] #return the list of paths
        if start not in graph: #if the start element is not in the graph
            print( 'Not Found')#prints out not found
            return [] #return an empty list
        paths = [] #path list
        for node in graph[start]: #for node in the graph

            if node not in path: #if not in the path
                newpaths = self.dfsSearch(graph, node, end, path) #new paths we found

                for newpath in newpaths: #for each new path in the list of new paths
                    paths.append(newpath) #add the new path to our list of paths

        paths.sort() #sort our paths
        self.cycle_exists(graph)
        #print(self.cycle_exists(graph))

        return paths #return our paths


    def cycle_exists(self, graph): # -graph is our graph.
        color = { node : "white" for node in graph}  #color all nodes white to begin with
        found_cycle = False # found_cycle set to false

        for node in graph: # for each node in graph.
            if color[node]:#if the color[node] is white
                self.dfs_visit(graph, node, color, found_cycle) #we call the dfs_visit method
            if found_cycle:#if a cycle is found
                found_cycle = True
                break#break
        return found_cycle #return the true or false

    def dfs_visit(self,graph, node, color, found_cycle):
        #print(color)
        if found_cycle: # if a cycle is found return to the cycle_exists method
            return
        color[node] = "gray"#else color the node gray
        for neighbor in graph[node]: #for every neighbor in the graph of the node
            if color[neighbor] == "gray": #If neighbor is gray
                found_cycle = True # then a cycle exists.
                return
            if color[neighbor] == "white": #if the neighbor is white
                #print(color[neighbor])
                self.dfs_visit(graph, neighbor, color, found_cycle)# call dfs_visit .

        color[node] = "black"# color the original node black

GraphDriver.py 从顶点导入 * 导入系统

class GraphDriver:
    def __init__(self):
        self.graph = Graph()

def main():
    graph = Graph()
    inFile = sys.argv[1]
    d = graph.readInputGraph(inFile)

    userInput = input("Enter a source and destination:")

    dog = userInput.split(" ", -1)


    for path in graph.dfsSearch(d, dog[0], dog[1]):
        print(path)




if __name__ == '__main__':
    main()

输入.txt 0 1 0 6 1 2 1 5 2 3 2 4 4 3 4 0 5 4 6 5

【问题讨论】:

    标签: cycle depth-first-search


    【解决方案1】:

    您的代码的问题在于,它期望布尔变量found_cycle 通过引用传递给dfs_visit。但是,Python 确实按值传递(linklink)。因此,当dfs_visit将参数found_cycle设置为True时,该修改不会影响调用者传入dfs_visitfound_cycle变量。

    您可以通过更改 dfs_visit 返回是否找到循环来解决此问题:

        def cycle_exists(self, graph):
            color = { node : "white" for node in graph}
    
            for node in graph:
                if color[node] == "white":
                    if self.dfs_visit(graph, node, color):
                        return True
    
            return False
    
        def dfs_visit(self,graph, node, color):
            color[node] = "gray"
    
            for neighbor in graph[node]:
                if color[neighbor] == "gray":
                    return True
                if color[neighbor] == "white":
                    if self.dfs_visit(graph, neighbor, color):
                        return True
    
            color[node] = "black"
            return False
    

    背景资料

    关于布尔变量的传递,考虑下面的例子:

    def f(my_bool):
      my_bool = True
    
    my_bool = False
    f(my_bool)
    
    print(my_bool)
    

    此代码将打印False。在全局范围内,变量my_bool 初始化为False,然后传递给f。它是按值传递的,因此在f 中,参数my_bool 接收值False。但是,这个变量my_boolf 之外的变量my_bool 无关。所以修改f里面的my_bool不会影响f外面的my_bool

    请注意,这并不意味着您不能传递对对象的引用,只是引用按值传递。考虑以下示例:

    class MyObject:
      def __init__(self):
        self.x = 13
    
    def f(my_object):
      my_object.x = 17
    
    def g(my_object):
      my_object = MyObject()
      my_object.x = 19
    
    my_object = MyObject()
    
    print(my_object.x)
    f(my_object)
    print(my_object.x)
    g(my_object)
    print(my_object.x)
    

    这个例子打印:

    13
    17
    17
    

    在全局范围内,变量my_object 使用MyObject 的实例进行初始化。 MyObject的构造函数将成员变量x初始化为13,也就是第一个打印出来的值。然后将my_object 变量传递给函数f。该变量是按值传递的,因此f 中的变量my_object 与全局范围内的变量my_object 是不同的变量。但是,两者都指向MyObject 的同一个实例。所以当f设置my_object.x = 17时,全局范围内的下一个print会显示这个值。

    接下来,在全局范围内,变量my_object 被传递给g。同样,变量是按值传递的,因此g 中的变量my_object 与全局范围内的变量my_object 不同,但两者都指向MyObject 的同一个实例。在g 中,my_object 变量随后被分配了一个MyObject 的新实例。这不会影响全局范围内的my_object,它仍然指向MyObject 的前一个实例。因此,全局范围内的最终print 仍将显示已分配给MyObject 的第一个实例的x17,而不是在g 中分配给xMyObject 的第二个实例中。

    所以这就是为什么例如您的 color 变量不受与您的 found_cycle 变量相同的问题的影响。对于color,您将每次调用的按值传递给dfs_visit,对于found_cycle,但您永远不会在dfs_visit 中为color 分配新值。因此,对color 所做的修改与cycle_exists 函数中的原始color 变量所指向的对象相同。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-08-19
      • 2021-08-04
      • 2015-06-24
      • 1970-01-01
      • 2016-01-27
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多