通过图表

时间:2017-12-03 08:03:42

标签: haskell

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

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                         

我知道这不是最美丽的解决方案,只是我提出的第一个解决方案。

EDIT2: 但是这个算法的一个问题是当图表看起来像这样:

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不应该包含在解决方案中。 有什么药物吗?

1 个答案:

答案 0 :(得分:2)

您的递归步骤

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

看起来很奇怪。您尝试处理n是路径上有效中间节点的情况。因此,对于递归,您需要在n日志中收集visited。问题是,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)