关于适配指定类型签名的通用DFS(基于Set)算法实现咨询
适配目标类型的通用DFS实现
好的,我来帮你把这个基于Set的DFS实现适配到目标类型签名上。先梳理下原代码的核心逻辑:它用栈维护待访问节点,借助Set高效跟踪已访问节点避免重复遍历,同时把未访问的后继节点放到栈前部来保证深度优先的遍历顺序。
下面是适配后的完整实现,完全贴合dfs :: (Ord a) => (a -> [a]) -> a -> [a]的类型要求:
import qualified Data.Set as Set dfs :: (Ord a) => (a -> [a]) -> a -> [a] dfs succ start = loop [start] (Set.singleton start) where loop [] _ = [] loop (x:xs) visited = x : loop (newNodes ++ xs) (Set.union visited newSet) where -- 将后继节点列表转换为Set,方便高效去重和差集计算 succSet = Set.fromList (succ x) -- 筛选出当前节点的未访问后继节点集合 newSet = Set.difference succSet visited -- 转回列表并放到栈前部,维持深度优先的遍历顺序 newNodes = Set.toList newSet
关键修改点说明
- 类型签名匹配:直接把原实现中
(a -> Set a)的参数调整为(a -> [a]),完全符合目标签名要求。 - 后继节点转换:新增
succSet = Set.fromList (succ x)这一步——因为原实现依赖Set的高效差集操作来避免重复访问,现在把输入的后继列表转成Set,就能复用原有的高效逻辑,避免用列表elem做O(n)的低效重复检查。 - 核心逻辑复用:栈的维护、已访问集合的更新、递归终止条件完全沿用原实现的逻辑,确保DFS的正确性和无重复遍历的特性。
测试示例
我们可以用一个简单的无向图来验证:
-- 节点1连接2和3,节点2、3都连接4,节点4无后继 graph :: Int -> [Int] graph 1 = [2, 3] graph 2 = [1, 4] graph 3 = [1, 4] graph 4 = [2, 3] graph _ = []
调用dfs graph 1会输出[1,2,4,3](由于Set的有序性,后继节点会按Ord顺序排列,最终遍历顺序符合深度优先的预期)。
补充说明
如果不想依赖Set,理论上也可以用列表跟踪已访问节点,但elem操作是O(n)复杂度,对于大规模图来说效率会大幅下降。而目标类型签名带有Ord a约束,用Set是最优选择——既利用了Ord带来的高效集合操作,又保证了算法的正确性。
内容的提问来源于stack exchange,提问作者Leo Zhang
相关产品推荐
相关产品推荐

