Haskell 函数拓扑排序
在拓扑排序算法中,我们执行DFS并在遇到节点时将其推送到链表上。我能想到的功能上实现这一点的唯一方法是将列表作为参数传递到调用中,这很难看而且效率很低(即,它显示在大O中,因为复制列表是O(n).在Haskell中,人们将如何惯用这种方法?讨论这个确切的问题,作为如何以函数式方式进行图形算法的第一个示例: [编者按:本文前面介绍了符号Haskell 函数拓扑排序,haskell,Haskell,在拓扑排序算法中,我们执行DFS并在遇到节点时将其推送到链表上。我能想到的功能上实现这一点的唯一方法是将列表作为参数传递到调用中,这很难看而且效率很低(即,它显示在大O中,因为复制列表是O(n).在Haskell中,人们将如何惯用这种方法?讨论这个确切的问题,作为如何以函数式方式进行图形算法的第一个示例: [编者按:本文前面介绍了符号c&vg,作为一种“图形上的模式匹配”:c&v g匹配包含顶点的图形v,将c绑定到给定顶点内外边的上下文,并将g绑定到删除该节点及其所有边的图形。在Haskell库
c&vg
,作为一种“图形上的模式匹配”:c&v g
匹配包含顶点的图形v
,将c
绑定到给定顶点内外边的上下文,并将g
绑定到删除该节点及其所有边的图形。在Haskell库中,这由函数实现。]
该算法的工作原理如下:dfs
停止而不返回任何节点。相反,如果仍然有必须访问的节点,dfs
尝试在参数图中定位第一个节点(v
)的上下文。如果可能的话(第二个方程式),每当参数图中包含v
时,就会出现这种情况,v
是结果节点列表上的下一个节点,搜索将继续在剩余的图g
上进行,并在剩余的节点列表vs
之前访问v
的后续节点所有其他节点的ont导致dfs
有利于在深度而不是广度上进行搜索。最后,如果v
无法匹配(最后一行),dfs
继续搜索剩余的节点列表vs
。请注意,只有当图中不包含v
时,才会出现最后一种情况,否则第二个等式中的模式会匹配
由于复制和粘贴不正常,我不得不转录这篇文章;几乎可以肯定,任何拼写错误都是我的,而不是马丁·厄维格的
正如您所建议的,剩余要访问的节点列表作为一个参数传入。但是,您声称这是低效的,这在我看来似乎不符合目标;构建新列表的成本是O(length(suc)
)--但无论如何,您必须访问所有这些节点至少一次,才能完成深度优先搜索,因此渐进成本是不可避免的
上面链接的整篇文章花了相当多的时间讨论渐近性和效率,而且(在我看来)非常有启发性,因此我鼓励您阅读。您为什么要复制列表?这一行给我带来了一些严重的误解。如果我理解正确,这个答案只是针对DFS。我说的是拓扑排序,在发现顶点时必须将其附加到列表中。我不明白这是怎么回事可以用函数式的方式来完成。@ReneéG我链接的论文接着讨论了如何在深度优先搜索的基础上实现toposort。不过,它比StackOverflow的答案要长一点,所以我真的鼓励您阅读它。它有丰富的信息,而且非常容易理解。
dfs :: [Node] -> Graph a b -> [Node]
dfs [] g = []
dfs (v:vs) (c &v g) = v:dfs (suc c++vs) g
dfs (v:vs) g = dfs vs g