我有一个树的数据结构:
数据树a = NodeT a(树a)(树a)| EmptyT
我需要创建一个返回列表列表的函数,其中列表的每个元素代表树的一个级别。例如,从中:
1
/ \
2 3
/ \ / \
4 5 6 7
对此:[[1],[2,3],[4,5,6,7]]
该功能必须具有以下形式:
f :: Tree a -> [[a]]
如何使用递归?
人
由于
答案 0 :(得分:5)
您递归计算级别并始终逐点合并两个子树中的列表(因此,相同深度的所有切片都会合并在一起)。
f :: Tree a -> [[a]]
f EmptyT = []
f (NodeT a t1 t2) = [a] : merge (f t1) (f t2)
merge :: [[a]] -> [[a]] -> [[a]]
merge [] ys = ys
merge xs [] = xs
merge (x:xs) (y:ys) = (x ++ y) : merge xs ys
如果树完整(从根到列表的所有路径长度相同),那么您可以使用zipWith (++)
作为merge
。
答案 1 :(得分:3)
比接受的解决方案稍微复杂一点,但我认为我的内存消耗方面可能更好(有点晚了,所以请自行检查)。
直觉来自Chris Okasaki "Breadth-First Numbering: Lessons from a Small Exercise in Algorithm Design"的精彩论文。您可以在功能语言中详细了解树的广度优先树遍历。
我添加"列表列表"分裂,可能有更好的方法:
module Main where
data Tree a = NodeT a (Tree a) (Tree a) | EmptyT
-- 1
-- / \
-- 2 3
-- / \ / \
-- 4 5 6 7
f :: Tree a -> [[a]]
f t = joinBack (f' [(t, True)])
type UpLevel = Bool
f' :: [(Tree a, UpLevel)] -> [(a, UpLevel)]
f' [] = []
f' ((EmptyT, _) : ts) = f' ts
f' ((NodeT a t1 t2, up) : ts) = (a, up) : f' (ts ++ [(t1, up)] ++ [(t2, False)])
joinBack :: [(a, UpLevel)] -> [[a]]
joinBack = go []
where
go acc [] = [reverse acc]
go acc ((x, False) : xs) = go (x : acc) xs
go acc ((x, True) : xs) = reverse acc : go [] ((x, False):xs)
main :: IO ()
main = do
let tree = NodeT 1 (NodeT 2 (NodeT 4 EmptyT EmptyT) (NodeT 5 EmptyT EmptyT))
(NodeT 3 (NodeT 6 EmptyT EmptyT) (NodeT 7 EmptyT EmptyT))
:: Tree Int
print (tail (f tree))