在DFS有向图中查找循环

时间:2015-10-29 18:26:49

标签: cycle depth-first-search

我试图使用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     来自Vertex import *     import sys

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()

INPUT.TXT 0 1 0 6 1 2 1 5 2 3 2 4 4 3 4 0 5 4 6 5

1 个答案:

答案 0 :(得分:0)

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

您可以通过更改 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_boolFalse 初始化,然后传递给 f。它是按值传递的,因此在 f 中,参数 my_bool 接收值 False。但是,此变量 my_boolmy_bool 之外的变量 f 无关。因此修改 my_bool 中的 f 不会影响 my_bool 外的 f

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

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。该变量是按值传递的,因此 my_object 中的变量 f 与全局作用域中的变量 my_object 是不同的变量。但是,两者都指向 MyObject 的同一个实例。因此,当 f 设置 my_object.x = 17 时,全局作用域中的下一个 print 将显示此值。

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

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