Haskell中的拓扑排序

时间:2014-02-10 11:28:00

标签: sorting haskell directed-acyclic-graphs

我有一个数据类型Graph,如下所示:

data Graph w = Graph {vertices :: [(Char, w)],
                      edges :: [(Char, Char, w)]} deriving Show

这表示有向无环图。顶点有一个字符标识符(添加的第一个顶点为'a',第二个顶点为'b',依此类推)和权重。边是两个顶点标识符和一个权重。

我正在考虑使顶点更复杂,也许它们应该包含所有邻居的列表?

到目前为止,拓扑排序看起来像这样:

topological_ordering :: Graph w -> [Char]
topological_ordering (Graph v w) =
    let startingNodes = getStartNodes (Graph v w)
        emptyList = []
        sorted = sortAlgorithm startingNodes emptyList (Graph v w)
    in sorted

sortAlgorithm :: [Char] -> [Char] -> Graph w -> [Char]
sortAlgorithm startingNodes sorted (Graph v w) =
    | [] _ _ = []
    | (x:startingNodes) sorted (Graph v w) =
      let sorted = sorted ++ [x]
          neigbours = findNeighbours (Graph v w) x

getStartNodes :: Graph w -> [Char]
getStartNodes (Graph v w) =
    let set1 = Set.fromList $ firstNodes w
        set2 = Set.fromList $ secondNodes w
        startNodes = Set.toList $ Set.difference set1 set2
    in  startNodes

firstNodes :: [(Char, Char, w)] -> [Char]
firstNodes [] = []
firstNodes (x:xs) = selectFirst x:firstNodes xs

secondNodes :: [(Char, Char, w)] -> [Char]
secondNodes [] = []
secondNodes (x:xs) = selectSecond x:secondNodes xs

从那里我有点失落。我真的不知道如何完成sortAlgorithm,因为我希望它是递归的(或者使用foldl / foldr?)。应该以另一种方式实现Graph的数据类型还是应该继续?

几周前我刚开始使用haskell,但仍然感觉功能编程有点丢失。

谢谢

2 个答案:

答案 0 :(得分:7)

您可能想看看它在Data.Graph中的优雅程度。以下是算法的概述:

topSort      :: Graph -> [Vertex]
topSort       = reverse . postOrd

postOrd      :: Graph -> [Vertex]
postOrd       = postorderF . dff

postorder :: Tree a -> [a]
postorder (Node a ts) = postorderF ts ++ [a]

postorderF   :: Forest a -> [a]
postorderF ts = concat (map postorder ts)

-- | A spanning forest of the graph, obtained from a depth-first search of
-- the graph starting from each vertex in an unspecified order.
dff          :: Graph -> Forest Vertex
dff g         = dfs g (vertices g)

-- | A spanning forest of the part of the graph reachable from the listed
-- vertices, obtained from a depth-first search of the graph starting at
-- each of the listed vertices in order.
dfs          :: Graph -> [Vertex] -> Forest Vertex

也就是说,图的拓扑排序是图的生成森林的后序遍历(反之)。

以下是如何使用它的示例:

  import qualified Data.Graph as G

  {-
     5 --> 7
     |     |
     v     V
     1 --> 4 --> 8
  -}

  (myGraph,vertexToNode,keyToVertex) = G.graphFromEdges [
      ("node4",4,[8]),     -- the first component can be of any type
      ("node8",8,[]),
      ("node7",7,[4]),
      ("node5",5,[1,7]),
      ("node1",1,[4])
   ]

  sorted = map vertexToNode $ G.topSort myGraph
  -- [("node5",5,[1,7]),("node7",7,[4]),("node1",1,[4]),("node4",4,[8]),("node8",8,[])]

答案 1 :(得分:3)

您是否有一个可靠的算法方法来解决拓扑排序问题?有不同的可能性;最着名的两个可能是:

  • 在图表上执行DFS,并按照完成时间的降序对顶点进行排序。所以:如果你已经有了DFS,那就调整它输出结束时间并按降序排序顶点。

  • 另一种方法要求您将传入的,尚未处理的边数存储到每个顶点中(这可能需要一些预处理,通常是一次图遍历 - 让我们为每个顶点调用相应的字段“边缘计数器”)。起始节点 - 当然 - 具有边计数器= 0.作为下一个顶点,您只能选择边计数器设置为0的那些。如果遇到边(a,b,w),则必须递减{的边计数器{1}}由1。

    请注意,第二种方法可以以一种列表b的方式实现,该列表最初只用起始节点填充。只要您递减candidates的边缘计数器并看到它现在为0,就会将b添加到b。在下一步中,您选择candidates的头部作为要处理的下一个顶点。

    要存储边数,您可以使用例如一个HashMap

这里有一些(非haskell,但可能是接近haskell)第二种方法的灵感:

candidates
相关问题