正如 svenningsson 所建议的,priority search queue 非常适合 Kruskal 和 Prim 的(至少作者在他的 paper 中声明了它。)Kruskal 的问题是它要求你有一个 O(log n) @987654323 @。 here 描述了具有纯函数式接口的联合查找数据结构,但它在内部使用可变状态,并且可能无法实现纯函数式实现,事实上,有几个问题不知道有效的纯函数式解决方案,如在this 相关的 SO 问题中讨论。
一个非纯的替代方案是在 ST monad 中实现 union-find 算法。在 Hackage 上搜索发现 equivalence 包适合我们的需要。以下是使用 equivalence 包中的 Data.Equivalence.Monad 的 Kruskal 实现:
import Data.Equivalence.Monad
import Data.Graph as G
import Data.List(sortBy)
import Data.Map as M
import Control.Monad(filterM)
import Data.Ord(comparing)
run = runEquivM (const ()) (const $ const ())
kruskal weight graph = run $
filterM go (sortBy (comparing weight) theEdges)
where
theEdges = G.edges graph
go (u,v) = do
eq <- equivalent u v
if eq then return False else
equate u v >> return True
可以这样使用:
fromL xs = fromJust . flip M.lookup (M.fromList xs)
testWeights = fromL [((1,2),1),((2,3),4),((3,4),5),((1,4),30),((1,3),4)]
testGraph = G.buildG (1,4) [(1,2),(2,3),(3,4),(1,4),(1,3)]
test = kruskal testWeights testGraph
运行测试给出:
[(1,2),(1,3),(3,4)]
需要注意的是,运行时间取决于在 O(1) 时间内运行的权重,但是fromL 创建了一个在 O(log(n)) 时间内运行的权重函数,这可以改进为 O(1 ) 时间通过使用数组或仅跟踪输入列表中的权重,但这并不是算法的真正部分。