我有一个有向图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?如果不是,你可以用什么论证来证明这种说法?
答案 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的前半部分,则不会计算其余部分。