树的深度(Haskell)

时间:2014-02-25 02:15:56

标签: haskell tree

我正在试图弄清楚如何计算Haskell中一般树的深度。我可以找出简单二叉树的解决方案,但不能用于任何叶子的一般树。

这是我对二叉树的代码。

--depth of a binary tree.

depth :: Tree a -> Int
depth Nil            = 0
depth (Node n x1 x2) = 1 + max (depth x1) (depth x2)

如何修改普通树?一般树木包含一系列树木,这就是我遇到困难的地方。

其次,我想把树变成一个列表(所以我可以做一些操作,比如计算总和等)。

同样,我可以想出二进制树,但不能用于普通树。

--tree into a list.

treeToList:: Tree a -> [a]
treeToList Nil = []
treeToList (Node n x1 x2)
         = collapse x1 ++ [n] ++ collapse x2

3 个答案:

答案 0 :(得分:4)

使用Foldable获取单个值,使用Functor映射函数

user2407038's good answer向您展示如何手动编写Foldable个实例,这是非常好的建议,您可以foldMap使用treeToList,而且还可以使用{-# LANGUAGE DeriveFunctor, DeriveFoldable #-} import Data.Monoid import Data.Foldable data Tree a = Node a [Tree a] deriving (Show,Functor,Foldable) 使方便的其他功能。

GHC允许您自动派生这些实例:

example :: Tree Int
example = Node 3 [Node 2 [], Node 5 [Node 2 [],Node 1 []],Node 10 []]

--   3
--   |
--   +--+-----+
--   2  5     10
--      |
--      +--+
--      2  1

让我们用一个例子来测试一下:

fmap

让我们使用> example Node 3 [Node 2 [], Node 5 [Node 2 [], Node 1 []], Node 10 []] > fmap (*10) example Node 30 [Node 20 [],Node 50 [Node 60 [],Node 10 []],Node 100 []] 将所有内容乘以10:

Monoid

使用mappend组合值

Monoid允许您组合(mempty)值,并且具有名为mempty = []的无操作/身份值。 列表是一个Monoid,mappend = (++)(+),数字是不止一种方式的moinoids,例如,使用Sum(*) monoid),使用{{1 (Product monoid),使用最大值(我必须手写Max幺半群)。

我们将使用foldMap使用我们想要使用的monoid来标记Ints:

> foldMap Sum example
Sum {getSum = 23}
> foldMap Product example
Product {getProduct = 600}
> foldMap Max example
Max {unMax = 10}

你可以根据自己的喜好定义自己的monoid - 这里是如何制作Max monoid:

instance (Ord a,Bounded a) => Monoid (Max a) where
    mempty = Max minBound
    mappend (Max a) (Max b) = Max $ if a >= b then a else b

你可以做出的最普遍的折叠

this great question中有很好的答案,Haskell的首席提问者MathematicalOrchid询问如何将折叠概括为其他数据类型。这个问题的答案很棒,值得一读。

广义fold只是用函数替换数据类型的每个构造函数,并计算得到一个值。

手动方式是查看每个构造函数的类型,并创建一个函数,它接受一个函数参数来匹配每个构造函数和一个参数本身的参数,并返回一个值。

<强>示例:

[]有两个构造函数,(:) :: a -> [a] -> [a][] :: [a]所以

foldList :: (a -> l -> l) ->    l      -> [a] -> l
foldList    useCons         useEmpty      = f where 
       f (a:as) = useCons a (f as)
       f [] = useEmpty

Either a b有两个构造函数,Left :: a -> Either aRight :: a -> Either所以

foldEither :: (a -> e) -> (b -> e) -> Either a b -> e
foldEither    useLeft      useRight    = f where 
       f (Left a) = useLeft a
       f (Right b) = useRight b  

树的广义折叠

generalFold :: (a -> [t] -> t) -> Tree a -> t
generalFold useNode = f where
     f (Node a ts) = useNode a (map f ts)

我们可以用它来做任何我们想做的事情到树上:

-- maximum of a list, or zero for an empty list:
maxOr0 [] = 0
maxOr0 xs = maximum xs

height :: Tree a -> Int
height = generalFold maxPlus1 where
      maxPlus1 a as = 1 + maxOr0 as

sumTree = generalFold sumNode where
      sumNode a as = a + sum as

productTree = generalFold productNode where
      productNode a as = a * product as

longestPath = generalFold longest where
      longest a as = a + maxOr0 as

让我们测试一下:

ghci> example
Node 3 [Node 2 [],Node 5 [Node 2 [],Node 1 []],Node 10 []]
ghci> height example
3
ghci> sumTree example  -- 3 + sum[2, 5+sum[2,1], 10] = 3+2+5+2+1+10
23
ghci> productTree example  -- 3*(2*(5*(2*1))*10) = 3*2*5*2*1*10
600
ghci> longestPath example  -- 3 + maximum [2, 5+maximum[2,1], 10]
13
ghci> toList example -- 3 : concat [[2], 5:concat[[2],[1]], [10]]
[3,2,5,2,1,10]

答案 1 :(得分:2)

考虑将模式概括为列表:

data Tree a = Node a [Tree a] | Nil

depth Nil = 0
depth (Node _ [a]) = 1 + depth a
depth (Node _ [a,b]) = 1 + max (depth a) (depth b)
depth (Node _ [a,b,c]) = 1 + max (max (depth a) (depth b)) (depth c)
etc...

嗯,你所做的就是找到每个子树的深度(map depth),然后找到这些数字的最大值(maximum):

depth Nil = 0
depth (Node _ a) = 1 + maximum (map depth a)

您可以以相同的方式展平树,只需map超过子树:

treeToList (Node n a) = n : concat (map treeToList a)

你必须使用concat,因为map collapse返回一个列表列表而你只需要一个列表。或者,您可以为Foldable类型类定义一个实例,然后自动获取toList :: Foldable t => t a -> [a]

import Data.Foldable
import Data.Monoid

instance Foldable Tree where 
  foldMap f Nil = mempty
  foldMap f (Node a n) = f a `mappend` mconcat (map foldMap n)

如果仔细仔细检查foldMap的定义,您会发现它只是一个更通用的treeToList,其中:mappend替换为[] 1}} mempty。那么你可以根据幺半群treeToList编写([], ++)这是合乎逻辑的:

data List a = List {getList :: [a]}
instance Monoid (List a) where 
  mempty = List []
  mappend (List a) (List b) = List (a ++ b)

treeToList = getList . foldMap (List . (:[]))

答案 2 :(得分:1)

一些指示:

查看map函数,该函数允许您将函数应用于列表中的每个元素。在您的情况下,您希望将depth应用于子项列表中的每个Tree a

获得该部分后,您必须在列表中找到最大深度。谷歌搜索“haskell max of list”,你会发现你需要的东西。