【问题标题】:Get a list of paths through a graph获取通过图表的路径列表
【发布时间】:2017-12-03 20:10:32
【问题描述】:

我正在尝试通过具有给定起点和目的地的图形获取所有可能路径的列表。 图表如下:

data Node = N1 | N2 | N3 | N4 | N5 deriving (Show, Eq)

neighbor :: Node -> [Node]
neighbor N1 = [N2, N4, N5]
neighbor N2 = [N1, N3]
neighbor N3 = [N1, N4, N5]
neighbor N4 = [N5]
neighbor N5 = [N1]

问题是:给定一个起始节点和一个目的地节点(例如,起始节点 = N1 和目的地 = N4)所有可能的路线,没有循环,应该是从起点到目的地的结果。在给出的示例中,它将是:

[[N1, N2, N3, N4],[N1, N4]]

我试图解决这个问题的功能是:

generatePaths :: (Node -> [Node]) -> Node -> Node -> [[Node]]

这里第一个参数应该是邻居函数,第二个是起始节点,第三个是目标节点。

我的主要问题是我现在找到了如何遍历所有邻居并调用generatePaths 并将每个邻居作为新的开始节点的方法。

非常感谢任何帮助

编辑: 感谢 Krom 和 RoadRunner,我想出了一个实现。

dfs_h :: (Node -> [Node]) -> [Node] -> [Node] -> Node -> [[Node]]
dfs_h graph visited [] _ = [visited]
dfs_h graph visited (n:ns) end 
    | elem n visited = (dfs_h graph visited ns end)
    | elem end ns = [reverse (end:visited)] ++ (dfs_h graph visited (n:[neigh|neigh <- ns, neigh /= end]) end)
    | n == end = [reverse (end:visited)] ++ (dfs_h graph visited ns end)
    | otherwise = dfs_h graph (n:visited) ((graph n) ++ ns) end

dfs start end = filter (\x -> elem end x) (dfs_h neighbor [start] (neighbor start) end                         

我知道这不是最漂亮的解决方案,只是我想出的第一个。

编辑2: 但是这个算法的一个问题是当图形看起来像这样时:

neighbor :: Node -> [Node]
neighbor N1 = [N2, N3]
neighbor N2 = [N5]
neighbor N3 = [N4]
neighbor N4 = [N2, N1]
neighbor N5 = [N1]

并且应该找到从N1N4的路径,那么函数的结果是

[[N1, N2, N5, N3, N4]]

现在我不打算实施什么,因此不应包含在解决方案中的 N2N5 不会被包括在内。 有什么建议吗?

【问题讨论】:

  • 很遗憾,我不能给你一个错误的实现,因为我删除了它以从头开始。但我的方法是这样的辅助函数:generatePaths_h :: (Node -&gt; [Node]) -&gt; [Node] -&gt; [Node] -&gt; Node -&gt; Node -&gt; [[Node]] 其中第二个参数现在是通向当前节点的路径,第二个参数应该是仍然需要迭代的邻居列表。其余参数同generatePaths
  • 图是有向的吗?您尝试实现哪种算法来解决这个问题?
  • 如果图是有向的,就使用深度优先搜索算法。
  • 感谢 dfs 的提示。我已经考虑过了,但放弃了这个想法,因为我找不到生成列表列表(即路径列表)的方法。我在原始问题中发布了我的实现。

标签: haskell


【解决方案1】:

你的递归步骤

| otherwise = dfs_h graph (n:visited) ((graph n) ++ ns) end

看起来很奇怪。您尝试处理n 是您路径上的有效中间节点的情况。因此,对于递归,您在visited 日志中收集n。问题是,ns——当前步骤中更多中间节点的列表——将在递归步骤中处理。相反,它应该使用未修改的当前访问节点集来处理。

一个更简单的解决方案是将您的日志记录与计算的中间结果分开:

generatePaths :: (Node -> [Node]) -> Node -> Node -> [[Node]]
generatePaths successors start end = map reverse $ dfs [] [[]] start
  where
    dfs :: [Node] -> [[Node]] -> Node -> [[Node]]
    dfs visited acc next
      -- destination reached, add final step
      | next == end = step next acc
      -- circle detected, reject paths
      | next `elem` visited = []
      -- make one step, continue recursion
      | otherwise = concat . map (dfs (next:visited) (step next acc)) $ successors next

    -- add `next` to each of the current paths
    step :: Node -> [[Node]] -> [[Node]]
    step next = map ((:) next)

【讨论】:

  • 抱歉回复晚了。是的,我必须同意。您的功能比我的尝试更简单易懂。谢谢。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-10-04
  • 2018-08-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多