P87 - 图的深度优先遍历

2026-09-07 00:00    #Haskell   #99题   #图  

P87 - 图的深度优先遍历

Depth-first graph traversal

官方模块:Problems.P87 核心函数:depthFirst


← P86 图着色 | P88 连通分量 →


题目描述

从指定顶点出发,以深度优先顺序返回所有可达顶点。邻居来自 Set,因此这里按升序展开,结果可复现。

实现

方法一:标准实现

1depthFirst :: G -> Vertex -> [Vertex]
2depthFirst graph start = reverse (snd (visit start (Set.empty, [])))
3  where
4    visit vertex state@(seen, order)
5      | Set.member vertex seen = state
6      | otherwise = foldl (flip visit) state'
7          (Set.toAscList (neighbors vertex graph))
8      where state' = (Set.insert vertex seen, vertex : order)

状态 (seen, order) 显式在线程中传递;先记录当前顶点,再递归邻居,就是前序 DFS。

方法二:显式栈

 1depthFirstStack :: G -> Vertex -> [Vertex]
 2depthFirstStack graph start = visit Set.empty [start]
 3  where
 4    visit _ [] = []
 5    visit seen (vertex:stack)
 6      | Set.member vertex seen = visit seen stack
 7      | otherwise = vertex : visit seen'
 8          (Set.toAscList (neighbors vertex graph) ++ stack)
 9      where
10        seen' = Set.insert vertex seen

递归版本把待访问邻居放在调用栈中;这个版本将它们显式放入列表栈。邻居加入栈顶,所以仍会先深入当前分支。

方法对比

方法待访问状态
递归 DFSHaskell 调用栈
显式栈普通顶点列表

测试

1>>> let graph = toG (Paths [[1,2,3,4,5],[2,4],[6,7]])
2>>> depthFirst graph 1
3[1,2,3,4,5]
4>>> depthFirst graph 6
5[6,7]
6>>> depthFirstStack graph 1 == depthFirst graph 1
7True

每个顶点和边至多处理常数次,时间复杂度 O(V+E)O(|V|+|E|)

参考


← P86 图着色 | P88 连通分量 →