在深度优先搜索中跟踪和返回路径

时间:2021-12-20 21:33:52

So I have a problem that I want to use depth first search to solve, returning the first path that DFS finds. Here is my (incomplete) DFS function:

所以我有一个问题,我想使用深度优先搜索来解决,返回DFS找到的第一个路径。这是我的(不完整的)DFS功能:

    start = problem.getStartState()
    stack = Stack()
    visited = []
    stack.push(start)
    if problem.isGoalState(problem.getStartState):
        return something
    while stack:
        parent = stack.pop()
        if parent in visited: continue
        if problem.isGoalState(parent):
            return something
        visited.append(parent)
        children = problem.getSuccessors(parent)
        for child in children:
            stack.push(child[0])

The startState and goalState variables are simply a tuple of x, y coordinates. problem is a class with a variety of methods. The important ones here are getSuccessors (which returns the children of a given state in the form of a list of 3 item tuples. for this part of the problem though, only the first element of the tuple, (child[0]), which returns the state of the child in x, y coordinates, is important) and isGoalState (which provides the x, y coordinates of the goal state).

startState和goalState变量只是x,y坐标的元组。问题是一个有各种方法的类。这里重要的是getSuccessors(它以3项元组列表的形式返回给定状态的子节点。虽然这部分问题只是元组的第一个元素,(child [0]),返回x,y坐标中的子状态,重要)和isGoalState(提供目标状态的x,y坐标)。

So I THINK (difficult to test at this point), that this function, given proper implementation of everything else, will return once it has reached a goal state. Please let me know if I am missing something. My biggest issue, though, is WHAT to return. I want it to output a list of all of the states it takes to get to the goal state, in order from the beginning to the end. It doesn't seem like simply returning my stack will do the trick, since the stack will include many unvisited children. Nor will my visited list yield anything useful, since it is conceivable I could reach dead ends, have to backtrack, but still have the dead-end tuples in the visited list. How would I go about getting the list I desire?

所以我认为(在这一点上难以测试),这个功能,如果其他一切正确实现,将在它达到目标状态后返回。如果我错过了什么,请告诉我。不过,我最大的问题是什么回归。我想让它输出一个列表,列出从开始到结束的所有状态到达目标状态。它似乎不像简单地返回我的堆栈将做的技巧,因为堆栈将包括许多未访问的孩子。我的访问列表也不会产生任何有用的东西,因为可以想象我可以达到死胡同,不得不回溯,但仍然有访问列表中的死胡同。如何获得我想要的清单?

4 个解决方案

#1


32  

You are right - you cannot simply return the stack, it indeed contains a lot of unvisited nodes.

你是对的 - 你不能简单地返回堆栈,它确实包含许多未访问的节点。

However, by maintaining a map (dictionary): map:Vertex->Vertex such that parentMap[v] = the vertex we used to discover v, you can get your path.

但是,通过维护地图(字典):map:Vertex-> Vertex,使得parentMap [v] =我们用来发现v的顶点,你可以得到你的路径。

The modification you will need to do is pretty much in the for loop:

您需要做的修改几乎在for循环中:

    for child in children:
        stack.push(child[0])
        parentMap[child] = parent #this line was added

Later on, when you found your target, you can get the path from the source to the target (pseudo code):

稍后,当您找到目标时,您可以获得从源到目标的路径(伪代码):

curr = target
while (curr != None):
  print curr
  curr = parentMap[curr]

Note that the order will be reversed, it can be solved by pushing all elements to a stack and then print.

请注意,顺序将颠倒,可以通过将所有元素推送到堆栈然后打印来解决。

I once answered a similar (though not identical IMO) question regarding finding the actual path in BFS in this thread

我曾经回答了一个类似的(虽然不完全相同的IMO)关于在这个帖子中找到BFS中的实际路径的问题

Another solution is to use a recursive version of DFS rather then iterative+stack, and once a target is found, print all current nodes in the recursion back up - but this solution requires a redesign of the algorithm to a recursive one.

另一个解决方案是使用DFS的递归版本而不是迭代+堆栈,并且一旦找到目标,就会在递归中打印所有当前节点 - 但是此解决方案需要将算法重新设计为递归算法。


P.S. Note that DFS might fail to find a path to the target (even if maintaining a visited set) if the graph contains an infinite branch.
If you want a complete (always finds a solution if one exists) and optimal (finds shortest path) algorithm - you might want to use BFS or Iterative Deepening DFS or even A* Algorithm if you have some heuristic function

附:请注意,如果图形包含无限分支,则DFS可能无法找到目标的路径(即使维护访问集)。如果你想要一个完整的(总是找到一个解决方案,如果存在的话)和最优(找到最短路径)算法 - 如果你有一些启发式函数,你可能想要使用BFS或Iterative Deepening DFS甚至A *算法

#2


7  

Not specific to your problem, but you can tweak this code and apply it to different scenarios, in fact, you can make the stack also hold the path.

不是特定于您的问题,但您可以调整此代码并将其应用于不同的场景,事实上,您可以使堆栈也保持路径。

Example:

     A
   /    \
  C      B
  \     / \
   \    D E
    \    /
       F


graph = {'A': set(['B', 'C']),
         'B': set(['A', 'D', 'E']),
         'C': set(['A', 'F']),
         'D': set(['B']),
         'E': set(['B', 'F']),
         'F': set(['C', 'E'])}




def dfs_paths(graph, start, goal):
    stack = [(start, [start])]
    visited = set()
    while stack:
        (vertex, path) = stack.pop()
        if vertex not in visited:
            if vertex == goal:
                return path
            visited.add(vertex)
            for neighbor in graph[vertex]:
                stack.append((neighbor, path + [neighbor]))

print (dfs_paths(graph, 'A', 'F'))   #['A', 'B', 'E', 'F']

#3


4  

this link should help you alot ... It is a lengthy article that talks extensively about a DFS search that returns a path... and I feel it is better than any answer I or anyone else can post

这个链接应该对你有所帮助...这是一篇冗长的文章,广泛讨论了返回路径的DFS搜索......我觉得它比我或其他任何人都可以发布的任何答案都要好

http://www.python.org/doc/essays/graphs/

#4


1  

I just implemented something similar in PHP.

我刚刚在PHP中实现了类似的东西。

The basic idea behind follows as: Why should I maintain another stack, when there is the call stack, which in every point of the execution reflects the path taken from the entry point. When the algorithm reaches the goal, you simply need to travel back on the current call stack, which results in reading the path taken in backwards. Here is the modified algorithm. Note the return immediately sections.

其背后的基本思想如下:当存在调用堆栈时,为什么要维护另一个堆栈,在执行的每个点都反映从入口点获取的路径。当算法达到目标时,您只需返回当前的调用堆栈,这将导致读取向后的路径。这是修改后的算法。注意立即返回部分。

/**
 * Depth-first path
 * 
 * @param Node $node        Currently evaluated node of the graph
 * @param Node $goal        The node we want to find
 *
 * @return The path as an array of Nodes, or false if there was no mach.
 */
function depthFirstPath (Node $node, Node $goal)
{
    // mark node as visited
    $node->visited = true;

    // If the goal is found, return immediately
    if ($node == $goal) {
        return array($node);
    }

    foreach ($node->outgoing as $edge) {

        // We inspect the neighbours which are not yet visited
        if (!$edge->outgoing->visited) {

            $result = $this->depthFirstPath($edge->outgoing, $goal);

            // If the goal is found, return immediately
            if ($result) {
                // Insert the current node to the beginning of the result set
                array_unshift($result, $node);
                return $result;
            }
        }
    }

    return false;
}

#1


32  

You are right - you cannot simply return the stack, it indeed contains a lot of unvisited nodes.

你是对的 - 你不能简单地返回堆栈,它确实包含许多未访问的节点。

However, by maintaining a map (dictionary): map:Vertex->Vertex such that parentMap[v] = the vertex we used to discover v, you can get your path.

但是,通过维护地图(字典):map:Vertex-> Vertex,使得parentMap [v] =我们用来发现v的顶点,你可以得到你的路径。

The modification you will need to do is pretty much in the for loop:

您需要做的修改几乎在for循环中:

    for child in children:
        stack.push(child[0])
        parentMap[child] = parent #this line was added

Later on, when you found your target, you can get the path from the source to the target (pseudo code):

稍后,当您找到目标时,您可以获得从源到目标的路径(伪代码):

curr = target
while (curr != None):
  print curr
  curr = parentMap[curr]

Note that the order will be reversed, it can be solved by pushing all elements to a stack and then print.

请注意,顺序将颠倒,可以通过将所有元素推送到堆栈然后打印来解决。

I once answered a similar (though not identical IMO) question regarding finding the actual path in BFS in this thread

我曾经回答了一个类似的(虽然不完全相同的IMO)关于在这个帖子中找到BFS中的实际路径的问题

Another solution is to use a recursive version of DFS rather then iterative+stack, and once a target is found, print all current nodes in the recursion back up - but this solution requires a redesign of the algorithm to a recursive one.

另一个解决方案是使用DFS的递归版本而不是迭代+堆栈,并且一旦找到目标,就会在递归中打印所有当前节点 - 但是此解决方案需要将算法重新设计为递归算法。


P.S. Note that DFS might fail to find a path to the target (even if maintaining a visited set) if the graph contains an infinite branch.
If you want a complete (always finds a solution if one exists) and optimal (finds shortest path) algorithm - you might want to use BFS or Iterative Deepening DFS or even A* Algorithm if you have some heuristic function

附:请注意,如果图形包含无限分支,则DFS可能无法找到目标的路径(即使维护访问集)。如果你想要一个完整的(总是找到一个解决方案,如果存在的话)和最优(找到最短路径)算法 - 如果你有一些启发式函数,你可能想要使用BFS或Iterative Deepening DFS甚至A *算法

#2


7  

Not specific to your problem, but you can tweak this code and apply it to different scenarios, in fact, you can make the stack also hold the path.

不是特定于您的问题,但您可以调整此代码并将其应用于不同的场景,事实上,您可以使堆栈也保持路径。

Example:

     A
   /    \
  C      B
  \     / \
   \    D E
    \    /
       F


graph = {'A': set(['B', 'C']),
         'B': set(['A', 'D', 'E']),
         'C': set(['A', 'F']),
         'D': set(['B']),
         'E': set(['B', 'F']),
         'F': set(['C', 'E'])}




def dfs_paths(graph, start, goal):
    stack = [(start, [start])]
    visited = set()
    while stack:
        (vertex, path) = stack.pop()
        if vertex not in visited:
            if vertex == goal:
                return path
            visited.add(vertex)
            for neighbor in graph[vertex]:
                stack.append((neighbor, path + [neighbor]))

print (dfs_paths(graph, 'A', 'F'))   #['A', 'B', 'E', 'F']

#3


4  

this link should help you alot ... It is a lengthy article that talks extensively about a DFS search that returns a path... and I feel it is better than any answer I or anyone else can post

这个链接应该对你有所帮助...这是一篇冗长的文章,广泛讨论了返回路径的DFS搜索......我觉得它比我或其他任何人都可以发布的任何答案都要好

http://www.python.org/doc/essays/graphs/

#4


1  

I just implemented something similar in PHP.

我刚刚在PHP中实现了类似的东西。

The basic idea behind follows as: Why should I maintain another stack, when there is the call stack, which in every point of the execution reflects the path taken from the entry point. When the algorithm reaches the goal, you simply need to travel back on the current call stack, which results in reading the path taken in backwards. Here is the modified algorithm. Note the return immediately sections.

其背后的基本思想如下:当存在调用堆栈时,为什么要维护另一个堆栈,在执行的每个点都反映从入口点获取的路径。当算法达到目标时,您只需返回当前的调用堆栈,这将导致读取向后的路径。这是修改后的算法。注意立即返回部分。

/**
 * Depth-first path
 * 
 * @param Node $node        Currently evaluated node of the graph
 * @param Node $goal        The node we want to find
 *
 * @return The path as an array of Nodes, or false if there was no mach.
 */
function depthFirstPath (Node $node, Node $goal)
{
    // mark node as visited
    $node->visited = true;

    // If the goal is found, return immediately
    if ($node == $goal) {
        return array($node);
    }

    foreach ($node->outgoing as $edge) {

        // We inspect the neighbours which are not yet visited
        if (!$edge->outgoing->visited) {

            $result = $this->depthFirstPath($edge->outgoing, $goal);

            // If the goal is found, return immediately
            if ($result) {
                // Insert the current node to the beginning of the result set
                array_unshift($result, $node);
                return $result;
            }
        }
    }

    return false;
}