我有一个类似树的类型:
data Tree a = EmptyTree | Tree a [Tree a] deriving (Show, Ord, Eq)
freeTree :: Tree Integer
freeTree = Tree 2 [Tree 5 [], Tree 6 [Tree 8 [], Tree 9 []], Tree 7 []]
main = print freeTree
我要做的是编写一个可以像这样使用的函数:
trace freeTree
此树上的痕迹将返回:[2],[2,5],[2,6],[2,7],[2,6,8],[2,6,9]
基本上它的作用是:
保留已经在'堆栈上的节点列表。 (每个深度的根节点让我们来到这里)。每次到达新节点时,都会将一个列表作为堆栈节点列表++ current_node添加到结果列表中。
有人可以就如何做到这一点提出任何建议吗?
由于
答案 0 :(得分:3)
第一个(不是真正有效的实施):
trace :: Tree a -> [[a]]
trace t = trace' [([],t)] []
type Level a = [([a],Tree a)]
trace' :: Level a -> Level a -> [[a]]
trace' [] [] = [] -- current and next level empty? We're done!
trace' [] l = trace' l [] -- current level exhausted? Next level becomes current level and we construct a new level
trace' ((_,EmptyTree):ts) lu = trace' ts lu -- currently an EmptyTree? Skip it
trace' ((h,Tree t c):ts) lu = ht : trace' ts (lu++el) -- currently a tree? Enumerate and add childs
where ht = h++[t]
el = map (\x -> (ht,x)) c
该算法使用两个Level a
,即当前级别和下一级别。您始终首先在当前级别上迭代,并且对于当前级别中的每个项目,您将该级别的子级别添加到下一级别,直到当前级别用尽为止。这种方法的唯一问题是++
操作非常昂贵,特别是因为它们应用于左关联而非右关联。通过使用更紧凑的元组列表表示,可以使内存效率更高一些。
您可以通过使用 FIFO队列来提高效率,例如this one(让我们假设至少所有队列的接口都相同,以防万一你更喜欢另一个,你可以交换位置。
在这种情况下,代码将为:
type Level a = [([a],Tree a)]
type LevelFiF a = FIFO ([a],Tree a)
trace' :: Level a -> LevelFiF a -> [[a]]
trace' [] ln | isEmpty ln = []
| otherwise = trace' (toList ln) empty
trace' ((h,Tree t c):ts) ln = ht : trace' ts (foldl (flip enqueue) ln el)
where ht = h++[t]
el = map (\x -> (ht,x)) c
trace' (_:ts) ln = ht : trace' ts ln
你也可以使用Haskell的一个monadic队列来提高效率。
答案 1 :(得分:2)
我们可以认为我们有一个trie,其中每个节点都标记一个有效的单词,然后工作就是招募单词:
trace :: Tree a -> [[a]]
trace (Tree a ts) = [a] : map (a:) (trace =<< ts)
trace Empty = []
tree1 = Tree 1 [Tree 2 [ Tree 3 [ Tree 4 [Tree 5 [] ]]]]
tree2 = Tree 2 [Tree 5 [], Tree 6 [Tree 8 [], Tree 9 []], Tree 7 []]
-- trace tree1 = [[1],[1,2],[1,2,3],[1,2,3,4],[1,2,3,4,5]]
-- trace tree2 = [[2],[2,5],[2,6],[2,6,8],[2,6,9],[2,7]]
这是一种深度优先的解决方案,可以通过仅对当前单词所需的空间进行延迟处理。它不会以您指定的完全相同的顺序返回单词;如果严格的广度优先顺序很重要,那么你应该进行迭代深化:
traceIDeep :: Tree a -> [[a]]
traceIDeep t = concat $ takeWhile (not . null) $ map (`lvl` t) [0..]
where
lvl 0 (Tree a ts) = [[a]]
lvl l (Tree a ts) = map (a:) (lvl (l - 1) =<< ts)
lvl _ Empty = []
-- Now we have bfs order:
-- trace tree2 = [[2],[2,5],[2,6],[2,7],[2,6,8],[2,6,9]]
答案 2 :(得分:0)
基本上,您希望在trace
的递归调用中保持状态。所以考虑这样的函数签名:
-- First argument is the tree to trace
-- Second argument is the accumulated route
trace :: Tree a -> [a] -> [[a]]
通过这种结构,您可以有效地在此树上进行深度优先搜索。