首先遍历图广度,标记Haskell中的访问节点

时间:2014-01-12 12:46:45

标签: haskell graph-theory breadth-first-search

所以问题很简单。给定一个图形(我希望图形的结构在这个问题上没有多大关系),我该怎么做呢?

我最近问过一个关于生成列表的问题,其中每个元素都会在其末尾添加许多元素。答案应该有希望让我做一个我需要做BFS的队列。但是搜索需要另一个关键组件,它将节点标记为已访问,因此我们不会再次检查它们。这也需要在算法的执行上没有开销。无论是标记还是阅读。

由于Haskell不让我改变状态,我该如何去做呢?

(我不是在寻找将我的命令式代码翻译成Haskell的方法。惯用的Haskell解决方案会很棒。)

3 个答案:

答案 0 :(得分:7)

正如评论中所提到的,正确的方法是将图表与标记其节点分开。您需要使用某种集合容器。您基本上可以采取两种方法:

  1. 使用功能集。尽管在每次修改时都会创建一个新版本,但是功能数据结构的设计使得只有非常小的部分实际上会发生变化,并且大部分原始数据都是共享的。对于此类结构,插入/查找操作采用 O(log n)。但是来自无序容器HashSet针对速度进行了优化,并且对数的基数很大,因此对于大多数目的而言,因子就像常数一样。
  2. 使用ST monad(另请参阅this question)。在monad中你可以使用可变数据结构,而结果仍然是引用透明的。然后,您可以使用 hashtables 包中的ST-based hash tables。这允许您摆脱 log n 因子并在恒定时间内对哈希表执行操作。但是还有其他缺点,例如你的遍历必须是纯粹的。如果在其他monad中工作,则无法将其与ST操作交错。

答案 1 :(得分:6)

论文Inductive Graphs and Functional Graph Algorithms讨论了这些问题并且非常易读。它是fgl包的基础,具有an entire module for BFS-like queries。我强烈推荐这篇论文 - 这对我来说是一个令人大开眼界的读物(即,即使你的数据不是归纳的,“给出一个感应界面的基本核心思想是一个很棒的”) - 并进一步建议如果你可以使用你做的fgl包。但是,如果你不能,那么本文将告诉你足够为自定义数据类型编写算法,我敢肯定。

答案 2 :(得分:0)

我认为,最简单的方法是查看树形图中的任何级别。

  • 在任何级别,都有n个节点。
  • 此级别的BF遍历将遍历这n个节点,
  • BF遍历这些n个节点的子节点。

现在很容易翻译,(这段代码首先使图表变宽)。

toBFSList :: [Graph a] -> [Graph a]
toBFSList [] = [] -- trivial case
toBFSList gs = ns ++ fs 
    where ns = map extract gs -- "extract" may extract a single node
          cs = concat $ map children gs   -- get all the child nodes for all above node
          fs = toBFSList cs   -- for all children, do traversal