找到通过Haskell中DFS森林的最长路径

时间:2016-11-17 05:22:32

标签: haskell graph tree

我有一张图表,我试图找到最长的路径,但我不确定如何去做。我使用Data.Graph中的标准GraphEdge类型,图表是使用buildG函数生成的。我最初的想法是使用dfs来执行深度优先搜索,但这产生了一个Forest对象,我不确定如何操作。

如果它完全有帮助(我很抱歉我不能打印这个,showForest只显示字符串),这是我图上dfs命令的输出:< / p>

[Node {rootLabel = 87, subForest = [Node {rootLabel = 82, subForest = [Node {rootLabel = 70, subForest = []},Node {rootLabel = 83, subForest = [Node {rootLabel = 66, subForest = [Node {rootLabel = 72, subForest = [Node {rootLabel = 88, subForest = []}]}]},Node {rootLabel = 79, subForest = [Node {rootLabel = 69, subForest = [Node {rootLabel = 85, subForest = []},Node {rootLabel = 84, subForest = [Node {rootLabel = 86, subForest = []},Node {rootLabel = 73, subForest = [Node {rootLabel = 81, subForest = []}]}]}]}]}]},Node {rootLabel = 89, subForest = []}]}]}]

我找到了一些不同的函数来查找通过树的最长路径,例如在此answer中,但它们仅适用于Tree s,而不适用于Forest s,而我我不确定是否有可能在两者之间进行转换。

谢谢!

3 个答案:

答案 0 :(得分:3)

正如Shersh在他们的回答中解释的那样,深度优先搜索不足以解决您的问题(如果是,您可以使用您链接的答案中的generalFold,例如,重建最长的路径在森林的每棵树上)。另一种方法是从Data.Graph切换到 fgl ,它提供各种各样的图算法,包括广度优先搜索。请注意,The Haddock documentation的FGL相当简洁,因此您还需要查阅package homepage提供的有用的用户指南。在下面的演示中,我将使用bft函数来获取图表的广度优先生成树,这应该足以让您入门:

GHCi> import Data.Graph.Inductive.Graph
GHCi> import Data.Graph.Inductive.PatriciaTree 
GHCi> import Data.Graph.Inductive.Query.BFS
GHCi> :{
GHCi| test :: UGr
GHCi| test = mkUGraph [1..7] [(1,3),(1,2),(2,4),(3,5),(2,6),(5,2),(5,7)]
GHCi| :}
GHCi> prettyPrint test
1:()->[((),2),((),3)]
2:()->[((),4),((),6)]
3:()->[((),5)]
4:()->[]
5:()->[((),2),((),7)]
6:()->[]
7:()->[]
GHCi> bft 1 test
[[1],[2,1],[3,1],[4,2,1],[6,2,1],[5,3,1],[7,5,3,1]]

生成树本质上是从节点到所有可到达节点的路径列表。例如,如果您只想找到一条长度最大且不关心绘图的路径,那么您只需要:

GHCi> import Data.Ord
GHCi> import Data.List
GHCi> maximumBy (comparing length) (bft 1 test)
[7,5,3,1]

如果你关心抽奖,你需要做一些像groupBy这样的事情,但这并不会从根本上变得更加困难。

答案 1 :(得分:2)

我将首先解释dfs的作用。正如您在评论中提到的那样,您使用了buildG函数,因此我也会在答案中使用它。

dfs有两个参数:图形和根节点列表。然后dfs从列表中的每个顶点编号开始运行深度优先搜索,从列表中的那些节点返回可到达顶点的Tree。例如:

  1. 您有1个顶点的图形,并且您希望从1中查找路径。然后,您将只有一个Tree的列表,其中根标签为1且subforest为空。
  2. 您有2个顶点和边缘从1到2的图形,并且您希望从1找到路径。然后,您将只有一个Tree的列表,其中根标签为1,而subforest列表为2作为根标签。
  3. 您有3个顶点和边(1,2)和(1,3)的图形,并且您希望从1中找到路径。然后,您将只有一个Tree的列表,其中根标签为{{1和subforest是列表,其中包含两个树,相应地以2和3作为根标签。
  4. 以下 ghci 会话中演示了所有这些示例(甚至更多示例):

    1

    但是,不幸的是,λ: let g = buildG (1,1) [] λ: dfs g [1] [Node {rootLabel = 1, subForest = []}] λ: dfs g [2] *** Exception: Ix{Int}.index: Index (2) out of range ((1,1)) λ: let g = buildG (1,2) [(1,2)] λ: dfs g [1] [Node {rootLabel = 1, subForest = [Node {rootLabel = 2, subForest = []}]}] λ: dfs g [2] [Node {rootLabel = 2, subForest = []}] λ: let g = buildG (1,3) [(1,2), (1,3)] λ: dfs g [1] [Node {rootLabel = 1, subForest = [Node {rootLabel = 3, subForest = []},Node {rootLabel = 2, subForest = []}]}] 无法帮助您完成任务:从给定的顶点找到最远的顶点。在算法运行的方式中,它无法解决这些问题。假设你有4个顶点和边的图:

    1→2,2→3→4→4→4

    从1开始路径最长的顶点是3(此路径有2条边)。但dfs会根据您指定的边缘顺序返回不同的结果。

    dfs

    这不是特定实施的问题。这就是λ: let g = buildG (1,4) [(1,2), (1,4), (2,3), (3,4)] λ: dfs g [1] [Node {rootLabel = 1, subForest = [Node {rootLabel = 4, subForest = []},Node {rootLabel = 2, subForest = [Node {rootLabel = 3, subForest = []}]}]}] λ: let g = buildG (1,4) [(1,4), (1,2), (2,3), (3,4)] λ: dfs g [1] [Node {rootLabel = 1, subForest = [Node {rootLabel = 2, subForest = [Node {rootLabel = 3, subForest = [Node {rootLabel = 4, subForest = []}]}]}]}] 的工作原理。在一般情况下,您无法使用dfs来解决您的特定问题。此外,这个问题无法通过重新排列列表中的边缘来修复,因为您不知道如何重新排列它们。

    您真正需要使用的是dfs算法 - 广度优先搜索。不幸的是,它没有在bfs库中实现。所以你需要从零实现整个算法或在某处找到实现(这里有一些讨论:Traversing a graph breadth first, marking visited nodes in Haskell)。

答案 2 :(得分:0)

应注意,图中最长的路径与最长路径不同 在生成树中。图形可以有许多生成树,而这些树没有 有相同的高度。 duplode提供的btf代码可以正确找到生成树中最长的路径。

例如,duplode中的图表答案 mkUGraph [1..7] [(1,3),(1,2),(2,4),(3,5),(2,6),(5,2),(5,7)] 比建议[1,3,5,7]的路径更长,路径为[1,3,5,2,6]

一个更简单的例子 mkUGraph [1,2,3] [(1,2),(1,3),(2,3)] 使用FGL btf 1将产生生成树[[1], [3,1], [2,1]],最长路径为[1,2,3](并且本身就是生成树)。

一般来说,找到最长的路径并不是微不足道的(实际上是NP难的)但是可以使用拓扑排序的节点相当简单的折叠来完成DAG。

请参阅 https://github.com/rpeszek/graph-exercise