【问题标题】:How can you use a breadth-first search to find the shortest path in a map?如何使用广度优先搜索在地图中找到最短路径?
【发布时间】:2013-01-13 17:54:23
【问题描述】:

为简单起见,假设我有图表:

O O P O |
O O O O O
O | O | O
O O O O O
A O O O O

我想使用广度优先搜索从 A 到 P 的最短路径,其中位置由 | 指定是禁区。我怎样才能达到这个结果?我一直看到用于查找某个位置的广度优先搜索(在这种情况下为 P),但我还没有看到任何用于存储路径距离和计算最短路径距离的实现(也没有有效的方法来存储以前访问过的位置和将他们排除在进一步审查之外)。此外,对于必须很大并且需要大量推送和弹出的图,通常建议使用什么队列?

【问题讨论】:

  • 可能与查找“A*”(具有“权重”)和 Djikstra 的算法有关 ..
  • @pst 嗯,是的,但是对于刚开始学习图形算法单元的学生来说,这个问题听起来很容易。 Bob 可能想先用概念上更简单的方法来做这件事。
  • 谢谢你们。我正在使用 BFS,所以我想在其中实现我的算法。我知道其他搜索算法,但这是我将使用的算法。

标签: c++ algorithm search


【解决方案1】:

我建议您阅读 Dijkstra 的算法,然后进行 A* 搜索。

下面是用 C++ 解决这个问题的简单方法:

  1. 创建一个初始化为 -1 的整数 std::vector(我们称之为“轨迹”),地图中的每个节点都有一个元素(在您的示例中大小为 25)。这将用于指示已经搜索了哪些节点,以及从哪里搜索了它们。 'trail' 值为 -1 的节点尚未被搜索到,而 'trail' 值为 8 的节点已从节点 8 中搜索到。

  2. 创建一个整数的 std::queue(我们称之为“search_queue”)。然后推送包含'A'的节点的ID,并将其'trail'值标记为自身。例如。如果 'A' 是节点 20,则将 trail[20] 设置为 20;这记录了路径从那个点开始。

  3. 从'search_queue'中弹出front node,依次查看每个邻居,如果trail值为-1,则将其id加入队列,且不受限制(不包含'|' )。

  4. 重复第 3 步,直到找到“P”(您已达到目标!),或者队列为空(没有有效路径)。

  5. 如果找到“P”,则使用“轨迹”向量将路径追溯到起点——每个节点将指向路径中的前一个节点,直到找到指向自身的节点.这就是你开始的地方,所以你现在有了完整的路径!

您不必计算任何距离,因为呼吸优先搜索的性质保证算法找到的第一个有效路径将是可能的最短路径。

【讨论】:

    【解决方案2】:

    我假设你有一个二维数组 A P O |价值观。如果未知,您将需要使用暴力搜索找到 A。

    对于路径,从 A 出发的最短路径可以通过创建一组 1-move 位置(即上方、右侧和可能的上方,如果允许对角线移动)来找到。当你这样做时,你想跟踪哪些位置已经被访问以避免返回到同一个方块,所以你需要对原始数组做一些事情(例如将访问的位置从“O”更改为“o”)或者有另一个数组只是为了这个。从 1-move 位置,您可以创建一组尚未访问过的合法 2-move 位置。继续前进,直到找到“P”。

    关于容器的选择:使用上面的算法,您不会弹出任何东西 - 可以只使用向量。

    作为优化,您可能也希望本地 P 并尝试对两者之间最明显的路径进行深度优先遍历 - 在您图示的情况下 [diagonally-up-right-]-else-up-else-right在到达目标行之前允许“向上”,在到达列之前允许“向右”。也可以做 right-else-up 或代替。

    【讨论】:

      【解决方案3】:

      广度优先搜索的美妙之处在于它会自动找到最短路径(您只需要在访问节点时跟踪您来自哪里)。 使用广度优先搜索,您总是在队列的开头找到最便宜的路径,而在最后找到昂贵的路径。只要您达到目标P,就可以保证路径长度最短。

      std::queue 绝对是实现它的最佳选择。

      回应您的评论:您有一个 nodes / vertices 队列(在您的情况下,这些是您的单元格)。当您访问一个节点时,您会将之前未访问过的所有邻居添加到队列中。要跟踪您访问了哪些节点以及路径来自何处,请在周围保留一个std::array/std::vector wherefrom;,每个节点都有一个元素。然后(伪代码!)

      take element v from queue
       for each neighbour x of v
          if wherefrom[x] != NULL
           wherefrom[x] = v
           add x to end of queue
      

      点击目标节点P 后,您可以通过wherefrom 回溯以找到路径。

      【讨论】:

      • 为什么要使用 std::deque 而不是包装版本 std::queue
      • 但是在检查队列时,如果我们遇到一条无路可走的路径,我们如何处理该路径?换句话说,“最终路径”存储在哪里,以便算法完成后可以轻松访问?可以使用哪些实现技术来使其在大量输入上快速运行?
      • 对不起,我还是有点糊涂。算法搜索了多条路径,所以我的问题是,当算法完成时,你如何确定什么是“the”路径?当然不是所有的位置都从队列中弹出,那么可以使用什么系统的方法来存储位置(当算法运行时)然后确定最终路径(当算法完成时)。如果我不理解一些明显的东西,我很抱歉,但这就是我的困惑所在。
      • @BobJohn See A*(我知道你想“自己做”,但请查看文章其余部分的伪代码和解释)。请参阅下面有关启发式要求的部分 - 例如任何节点都没有“扭曲门”。还要考虑启发式 always 何时返回 0(这是 Djikstra 的)。
      【解决方案4】:

      一旦你有了地图:

      O O P O |
      O O O O O
      O | O | O
      O O O O O
      A O O O O
      

      定义图,1表示是否有边:

      1 1 P 1 0
      1 1 1 1 1
      1 0 1 0 1
      1 1 1 1 1
      A 1 1 1 1
      

      在哪里a_ij = 0 iff m_ij = | and elsewhere a_ij = 1

      然后运行Dijkstra's algorithmBellman-ford algorithm

      如果你坚持使用BFS,这里解释一下为什么它也能正常工作:

      How does a Breadth-First Search work when looking for Shortest Path?

      【讨论】:

      • "如果你坚持使用 BFS" - Dijkstra 在未加权图上的算法等价于 BFS...
      猜你喜欢
      • 1970-01-01
      • 2017-06-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-12-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多