【问题标题】:Searching through a multi-branch graph and returning a path [C#]搜索多分支图并返回路径 [C#]
【发布时间】:2017-09-04 15:10:45
【问题描述】:

在我的情况下,我有领土对象。每个领土都知道它们通过数组连接到哪些其他领土。以下是这些地区在地图上的可视化效果:

如果您要在图表上绘制连接,它们将如下所示:

假设我有一个单位驻扎在领土[b],我想将它移动到领土[e],我正在寻找一种方法来搜索这个图表并返回一个代表我单位路径的最终数组在领土[b] 必须采取。在这种情况下,我会寻找它返回

[b, e].

如果我想从区域[a] 到区域[f] 那么它会返回:

[a, b, e, f].

我喜欢示例,但即使只是将我指向正确方向的帖子也会受到赞赏。提前致谢! :)

【问题讨论】:

    标签: search graph path-finding


    【解决方案1】:

    您以前听说过广度优先搜索 (BFS) 吗?

    基本上,您只需将初始区域(在您的示例中为“a”)放入一个空队列 Q

    您需要的第二个数据结构是一个布尔数组,其中包含与您的地区一样多的元素,在本例中为 9。它有助于记住我们已经检查过哪些地区。我们称它为 V(代表“已访问”)。它需要进行如下初始化:所有元素都等于假,除了与初始方块对应的元素。对于所有区域 t,我们有 V[t] = false,但 V[a] = true 因为“a”已经在队列中。

    您需要的第三个也是最后一个数据结构是一个数组来存储父节点(即我们来自哪个节点)。它还具有与您的领土一样多的元素。我们称它为 P(表示“父”),每个元素最初都指向自己,即对于 P 中的所有 t,设置 P[t] = t。

    那么,就很简单了:

    while Q is not empty:
       t = front element in the queue (remove it also from Q)
       if t = f we can break from while loop //because we have found the goal
       for all neighbors s of t for which V[s] = false:
           add s into the back of Q //we have never explored the territory s yet as V[s] = false
           set V[s] = true //we do not have to visit s again in the future
    
           //we found s because there is a connection from t to s
           //therefore, we need to remember that in s we are coming from the node t
           //to do this we simply set the parent of s to t:
           P[s] = t
    

    您现在如何阅读解决方案?

    只需检查 f 的父级,然后检查 that 的父级,然后检查 that 的父级,依此类推,直到找到开头。一旦找到一个以自身为父元素的元素(请记住,我们最初让它们指向自身),您就会知道开始是什么,或者您也可以将其与 a 进行比较。 基本上,你只需要一个空列表 L,将 f 添加到其中,然后

    while f != a:
        f = P[f]
        add f into L
    

    请注意,如果不存在路径,这显然会失败,因为 f 永远不会等于 a。 因此,这个:

    while f != P[f]:
        f = P[f]
        add f into L
    

    好一点。它利用了一个事实,即最初所有领土都指向 P 中的自己。

    如果你用上面的例子在纸上尝试这个,那么你最终会得到 L = [f, e, b, a] 如果你只是把这个列表倒过来,那么你就得到了你想要的。

    我不懂 C#,所以我没有费心使用 C# 语法。我假设你知道用整数索引你的地区然后使用数组来访问它们是最简单的。

    您会很快意识到为什么会这样。它被称为广度优先搜索,因为您首先只考虑区域“a”的邻居,到它们的最短路径(只有一条边)并且只有在处理完所有这些之后,更远的区域才会出现在队列中(从现在开始只有2条边)等等。这就是为什么我们为这个任务使用队列而不是堆栈之类的东西。

    此外,这在区域和边的数量上是线性的,因为您只需要(最多)查看每个区域和边一次(尽管来自两个方向的边)。

    我给你的算法与https://en.wikipedia.org/wiki/Breadth-first_search基本相同,只是添加了P数据结构来跟踪你从哪里来,以便能够弄清楚所走的路径。

    【讨论】:

    • 这是一个很棒的回应,我只是想感谢你!这个周末我会试试这个方法,然后回复你。
    • 不客气!如果您遇到任何困难,请告诉我们。
    • 好的,我知道这有点晚了,但我只是有时间来实现它并想跟进。事情大部分都是正确的,但是我觉得我做的事情不太正确。当我尝试执行 [f][b] 之类的操作时,一切正常并且返回正确的路径,但是如果我尝试像 [g][a] 这样的操作,我会遇到无限循环并挂起。我敢肯定这只是一个简单的误解你写的东西的例子。这是我写的:pastebin.com/03PWRu9h这是我的领地类:pastebin.com/sq0viDwb
    • 对于我这边的回复也很抱歉。我快速查看了代码,第 49 行似乎位于您设置 s.parent = t; 的错误位置您每次遍历邻居时都这样做,但只有在实际找到 s 时才应该这样做第一次。解决方案应该是简单地将第 49 行移到第 36 行下方的 if 分支中。
    猜你喜欢
    • 2020-06-22
    • 1970-01-01
    • 2015-02-21
    • 2018-03-25
    • 1970-01-01
    • 2021-02-11
    • 1970-01-01
    • 2013-02-19
    • 2011-11-14
    相关资源
    最近更新 更多