给定一组起始节点和后继函数(节点 - >设置节点),找到图的所有可到达节点

时间:2015-12-21 23:25:04

标签: haskell graph-theory graph-algorithm

我目前有以下实现,但我想知道是否有更快或不使用显式递归的解决方案:

import Data.Set (Set)
import qualified Data.Set as Set

reachable :: Ord node => Set node -> (node -> Set node) -> Set node
reachable startingNodes successors =
    go startingNodes Set.empty
  where
    go todo checked =
        case Set.minView todo of
            Nothing -> checked
            Just (node, todo') ->
                let new = successors node Set.\\ checked
                    todo'' = todo' `Set.union` new
                    checked' = Set.insert node checked
                in go todo'' checked'

1 个答案:

答案 0 :(得分:1)

您没有包含测试用例,所以我没有在任何示例上运行此解决方案:一些错误可能就在这里和那里。但是这样的事情应该有效:

  • 编写一个函数step,在探索的子图的边界上给定一组访问节点和一组节点,在该边界的所有方向上迈出一步。结果是一组新的访问节点和一个新的边界。

  • 使用unfoldr重复应用此功能,从而获得(增长的)受访节点集的列表。闭包只是该列表的last元素。

以下代码的作用如下:

{-# LANGUAGE ScopedTypeVariables #-}

module Reachability where

import Control.Monad (guard)
import Data.List (unfoldr)
import Data.Set (Set, union, empty, (\\))
import qualified Data.Set as Set

reachable' :: forall node. Ord node => Set node -> (node -> Set node) -> Set node
reachable' seed next = last $ seed : unfoldr step (seed, empty) where

  step :: (Set node, Set node) -> Maybe (Set node, (Set node, Set node))
  step (seed, visited) = guard (not $ null seed) >>
    let successors = foldr (union . next) empty seed
        newVisited = visited `union` seed
        newSeed    = successors \\ newVisited
    in return (newVisited, (newSeed, newVisited))