是否可以在Haskell中实现线性时间BFS?

时间:2014-11-04 21:49:08

标签: algorithm haskell functional-programming

我有一个有向图G作为邻接列表列表:

newtype Graph Int = Graph [(Int, [Int])]

G有n个顶点和m个边。我试图在Haskell中实现BFS算法,该算法在O(m)时间内运行(可能是摊销的),但是最好的解决方案我能够在O(m * log n)中运行并使用来自{的数据结构{1}}模块。

我对线性解决方案的想法如下:使用来自Data.Map的结构作为有效的FIFO队列,并按照命令式BFS执行所有操作,但我仍然困在我必须将节点标记为已访问的位置

我的问题是:是否可以在O(m)中运行的Haskell(或任何其他纯函数式语言)中实现BFS?如果不是,你可以用什么论证来证明这种说法?

1 个答案:

答案 0 :(得分:6)

我假设您的问题是您无法实现良好的队列。

看看Data.Sequence - 对于双端队列应该没问题,因为在序列结束时的操作非常快。向两端添加元素为O(1),从两端删除元素为O(1)

一旦你有了队列,它的表现就和DFS一样好。

您可以使用Map Int [Int](如果您的顶点是从Vector Int [Int]1的整数),而不是使用n

要将节点标记为已选中,您可以使用IntSet

这应该会让你O(V + E)

bfs :: V.Vector [Int] -> Int -> [Int]
bfs graph start = go IS.empty graph $ S.singleton start

go :: IS.IntSet Int -> V.Vector [Int] -> S.Sequence Int -> [Int]
go seen graph queue = 
  case S.viewL queue of
    S.EmptyL -> []
    vertex S.:< rest = vertex:(go seen' graph queue')
      where neighbors = filter (not . IS.member seen) (graph V.! vertex)
            seen' = S.insert vertex seen
            queue' = queue S.>< S.fromList neighbors

请注意,我们构建此列表的方式完全是懒惰的!因此,如果您只需要例如BFS的前半部分,则不会计算其余部分。