在列表中每层打印二进制树

时间:2010-11-01 12:05:04

标签: haskell

该函数应将图层中参数树的标记打印为图层列表。每个层中的节点和叶标记从左到右列出,也就是说,它是图层的最左边节点,列表的第一个元素,最右边的节点是列表的最后一个元素。 Ord类型的参数指示是否要发布从最小层到最大层(TopDown)的升序或从最大到最小层(BottomUp)的降序的层

data Tree = Leaf Integer | Node Integer Tree Tree

type Layer = [Integer]

data Ord = BottomUp | TopDown

wLayer :: Tree -> Ord -> [Layer] 

示例1:我们使用参数
来调用函数wLayer wLayer(节点1(节点2(叶21)(叶22))(节点3(叶31)(叶32)))TopDown 结果:[[1],[2,3],[21,22,31,32]]

示例2: wLayer(节点1(节点2(叶21)(叶22))(节点3(叶31)(叶32)))BottomUp 结果:[[21,22,31,32],[2,3],[1]]

我该如何实现这个呢?

修改

data Tree = Leaf Integer
          | Node Integer Tree Tree
type Layer = [Integer]
data Ord   = BottomUp | TopDown

writeLayer :: Tree -> Ord -> [Layer]
writeLayer Leaf x = [x]
writeLayer (Node x lt rt) BottomUp = (writeLayer rt BottomUp) ++ [x] ++ (writeLayer lt BottomUp)
writeLayer (Node x lt rt) TopDown  = [x] ++ (writeLayer lt TopDown) ++ (writeLayer rt TopDown)

这是我的程序,但是我怎么能解决它?

4 个答案:

答案 0 :(得分:2)

一些提示:

  • TreeLeaf的情况很简单
  • BottomUpTopDown之间的差异似乎是您是否反转Layer s
  • 列表
  • TreeNode时,您必须在子树上进行递归并以某种方式合并结果

编辑:好的,让我们专注于其中的第一个。

您对此案例的等式是

writeLayer Leaf x = [x]

首先,Leaf x需要在括号中,因为它是一个Tree值。

writeLayer (Leaf x) = [x]

其次,该等式需要反映writeLayer采用两个参数(如上所述,它只需要一个)。使用Leaf值,我们不关心返回结果的顺序 - 我们以任何一种方式给出相同的答案 - 但我们仍然必须采用参数。我们使用_表示我们不关心参数,并且不会使用它。

writeLayer (Leaf x) _ = [x]

第三,[x]Integer s的(单个元素)列表,但我们应该返回Layer个列表。我相信你可以弄清楚如何解决这个问题。

最后,请注意计算机给您的错误消息。了解他们。

答案 1 :(得分:2)

这是实现此目的的简单方法。它接受一个级别的所有节点并从中提取整数值,然后对这些相同节点的所有子节点进行递归。之后,您匹配Ord以确定是否需要撤消列表。

writeLayer t o =
    case o of
        BottomUp -> reverse $ makeLayer [t]
        TopDown -> makeLayer [t]
    where
        extract (Node i _ _) = i
        extract (Leaf i) = i
        children (Node _ a b) = [a, b]
        children _ = []
        makeLayer [] = []
        makeLayer ts = map extract ts : (makeLayer $ concat $ map children ts)

答案 2 :(得分:1)

保罗的回答给出了水平顺序遍历的核心定义 - 展开列表。 (练习:使用makeLayerData.List.unfoldr。)这也是我最喜欢的方式;见The Underappreciated Unfold

但它也可以递归地完成 - 作为树上的折叠。这些是通过类似于列表上的foldr来定义的,如下所示:

foldt :: (Integer->a) -> (Integer->a->a->a) -> Tree -> a
foldt f g (Leaf n)     = f n
foldt f g (Node n t u) = g n (foldt f g t) (foldt f g u)

然后,通过简单的树折给出水平顺序遍历,可能reverse

wLayer :: Tree -> Order -> [Layer] 
wLayer t o = (if o==BottomUp then reverse else id) (foldt single glue t)

我冒昧地将你的标志类型Order重命名为一个名称冲突的空白,并使其成为Eq的实例:

data Order = BottomUp | TopDown deriving Eq

函数single进行叶子的水平顺序遍历:

single :: Integer -> [Layer]
single n = [[n]]

glue将标签和两个孩子的遍历组合到一个节点的遍历中:

glue :: Integer -> [Layer] -> [Layer] -> [Layer]
glue n x y = [n] : longzipwith (++) x y

关键因素是函数longzipwith,它类似于zipWith,除了(i)结果的长度是较长参数的长度,而不是较短的,因此(ii)二元运算符必须是a->a->a

longzipwith :: (a->a->a) -> [a] -> [a] -> [a]
longzipwith f (a:x) (b:y) = f a b : longzipwith f x y
longzipwith f x     []    = x
longzipwith f []    y     = y

答案 3 :(得分:0)

这是我的程序

data Tree = Leaf Integer
    | Node Integer Tree Tree
type Layer = [Integer]
data DOrd   = BottomUp | TopDown
writeLayer :: Tree -> DOrd -> [Integer]
writeLayer (Leaf x) _ = [x]
writeLayer (Node x lt rt) BottomUp = (writeLayer rt BottomUp) ++ [x] ++ (writeLayer lt BottomUp)
writeLayer (Node x lt rt) TopDown  = [x] ++ (writeLayer lt TopDown) ++ (writeLayer rt TopDown)

呼叫:

*Main> writeLayer (Node 1 (Node 2 (Leaf 21) (Leaf 22)) (Node 3 (Leaf 31) (Leaf 32))) TopDown
[1,2,21,22,3,31,32]
*Main> writeLayer (Node 1 (Node 2 (Leaf 21) (Leaf 22)) (Node 3 (Leaf 31) (Leaf 32))) BottomUp
[32,3,31,1,22,2,21]

但我想先采取:[[1],[2,3],[21,22,31,32]]

第二:[[21,22,31,32],[2,3],[1]]