【问题标题】:Recursion in a Tree-like Structure树状结构中的递归
【发布时间】:2020-01-16 18:36:02
【问题描述】:

作为一个学校项目,我必须使用递归方法在迷宫中找到解决方案路径,我通常没有解决线性问题的递归算法问题,但是当涉及到多个选择/路径时,我不知道如何只找到一种解决方案。

问题参数:

  • 以矩阵表示的迷宫,有多种到达同一终点的方式
  • 起点

使用的语言:

  • F#

代码输出:

████████████████████
█     █           ██
█ ███ ███ █████ █ ██
█   █   █   █   █ ██
█ █ █ █ █ █ █ ███ ██
█     █Sxxxx  █   ██
█ █████ █ █x███ █ ██
█     █   █xxx█   ██
█ █ █ ███████x██████
█ █ █   █   █x    ██
█ █ ███ █ █ █x███ ██
█   █   █ █  x█   ██
█ ███ ███ █ █x█ █ ██
█            x█   ██
███ █████████x███ ██
███ █     █xxx█ █ ██
███ █ █ █ █x███ █ ██
███   █ █xxx█     ██
█████████x██████████
█████████E██████████

#:墙
: 路径
E:终点
S:起点

部分代码:

let rec dfs(x,y,path,visited) =
        let rec checkVisited point visited = 
            match visited with
            | [] -> false
            | (x,y)::xs -> if point = (x,y) then true else checkVisited point xs
        let adjacents = [(x,y+1);(x+1,y);(x,y-1);(x-1,y)]
        for point in adjacents do
            if point = this.endPoint then
                this.solutionPath <- path
            else
                if checkVisited point visited = false && this.checkPoint point && this.isWall point = false then
                    dfs(fst(point),snd(point),(path@[point]),(visited@[(x,y)]))

这是在迷宫中搜索解决方案的另一种方式(mooore 优化)

let rec dfs(x,y,path) =
            // setting the point in the matrix visited (setting it to 'false')
            matrix.[y].[x] <- (fst(matrix.[y].[x]),false)
            // getting the adjacents of the point
            let adjacents = [(x,y+1);(x+1,y);(x,y-1);(x-1,y)]
            // iterate the adjacents
            for (x1,y1) in adjacents do
                // if the adjacent is the end point set the soultion path
                if (x1,y1) = this.endPoint then
                    this.solutionPath <- path
                else
                    // else check if the point is in the matrix and is not yet visited
                    if this.checkPoint(x1,y1) && snd(matrix.[y1].[x1]) <> false && this.isWall(x1,y1) = false then
                        // execute recursively the method in the point and add the current poisition to the path
                        dfs(x1,y1,((x1,y1)::path))
        dfs(x,y,[])

我成功了!如果您在执行此操作时遇到任何问题,我会为您提供帮助(即使是其他语言)!

【问题讨论】:

  • 基本上,您正在尝试找到能够为您提供到达终点的最短路径的解决方案。因为有多个路径可以到达终点。因此,假设您正在尝试求解到 E 的最短路径。
  • 但是您要解决什么问题,例如您只想找出将您带到 E 的任何特定方式,或者您想找到到达 E 的所有可能方式。只是想了解。
  • 知道了,这个很简单,我F#不流利,如果我提供逻辑,会有帮助吗?
  • 您更擅长哪种语言?
  • 返回所有路径也是一种有据可查的方法。解决迷宫的第一项研究;进行有效的尝试;如果您仍然失败,请发布您的结果。你已经有了一个好的开始。

标签: algorithm recursion f# tree maze


【解决方案1】:

您当前的方法看起来基本没问题。但是因为您正在进行深度优先搜索,所以您遇到的主要问题是没有什么可以阻止您尝试像[(1,1);(1,2);(1,1);...] 这样的无限长路径,而不是进入更高效的路径。为避免这种情况,您可以扫描路径以查看建议的下一个点是否已经在其中(这需要时间最多与列表长度呈线性关系,这对于小型问题可能很好),或者传递访问点的集合作为递归函数的额外参数(应该允许更快的成员资格查询)。

您遇到的另一个问题是您没有任何方法可以组合您可以采用的不同分支的结果。一种简单的方法是将内部函数的返回类型更改为选项类型,并从顶部 if 返回 Some(path) 并将 else 重写为更像

[x, y+1
 x, y-1
 x+1, y
 x-1, y]
|> List.tryPick (fun (x',y') -> if this.checkPoint(x',y') then 
                                    sol(x', y', (x,y)::path)
                                else None)

这是递归地依次尝试每个可能的方向并返回第一个成功的方向。这不一定会返回最短路径,因为它是深度优先搜索。您还可以轻松创建一个变体,该变体返回所有可能路径的列表,而不是使用选项(最大的变化是使用 List.collect 而不是 List.tryPick),在这种情况下,您可以从列表中选择最短的解决方案,如果你想要,虽然这会做很多中间计算。

一个更复杂的变化是切换到广度优先搜索而不是深度优先,这样您就可以很容易地返回最短路径。从概念上讲,一种方法是跟踪到所有“可见”点的最短路径(仅从 [S,[]] 开始,以及一组尚未探索其子级的点(再次从 @ 开始) 987654328@)。然后,只要有要探索的点,就收集他们所有不同的孩子;对于每个还没有已知路径的孩子,添加路径并将其放入下一组要探索的孩子中。

【讨论】:

  • 所以我尝试了 DFS 并更新了答案,现在的问题是它给了我 Stackoverflow,可能是因为我不检查我是否已经在同一点,正如你建议的那样搜索了应该解决问题的 BFS,但我只在迭代模式下找到它,我必须递归解决问题,你能帮我解决这个问题吗?还是谢谢你的回复
  • @Stecco 我的最后一段说明了如何在概念上递归地进行广度优先搜索。如果遇到问题,您可以尝试并发布更新。但我认为修复你的深度优先搜索以检查该点是否已经出现在路径中会更容易考虑到你已经得到的。
  • 我现在已经用新代码更新了这个问题,检查点是否已经看到,但是当它返回解决方案时它不起作用,它只给出一个空数组
  • @Stecco - 你还有我在第二段中提到的问题;您没有将调用的结果组合到dfs,您只是运行前三个但忽略结果并返回最后一次调用的结果。
  • 是的,但我真的不知道如何解决这个问题,我不太了解树状结构的递归逻辑,能否请您更正代码以便我理解?我真的很感激
猜你喜欢
  • 2012-07-15
  • 2018-07-26
  • 2015-02-14
  • 2012-10-22
  • 2020-06-15
  • 1970-01-01
  • 2018-08-08
  • 2022-09-27
  • 2016-03-18
相关资源
最近更新 更多