确定Haskell中大多数元素出现的二叉树级别

时间:2015-12-29 05:30:53

标签: haskell binary-tree

给定二叉树,我试图找到具有最高出现次数的二叉树级别,例如Apple

以下是规格:

data Fruit = Peach | Apple
data BTree a = Empty | Node a (BTree a) (BTree a)
levelWithMaxApples :: BTree Fruit -> Int

我能够创建一个函数,在给定二叉树的情况下,它将计算其中Apple的出现次数。然而,我无法弄清楚如何找到Apple出现次数最多的级别,任何提示?

以下是一些测试:

tree1 = Node Peach Empty Empty

tree2 = Node Peach 
          (Node Peach
            (Node Apple
              Empty
              Empty)
            (Node Peach
              (Node Peach
                Empty
                Empty)
              (Node Peach
                Empty
                Empty)))
          (Node Apple
            (Node Apple
              Empty
              (Node Peach
                Empty
                Empty))
            Empty)

> levelWithMaxApples tree1
Nothing

> levelWithMaxApples tree2
2

这是我的尝试(在这种情况下,函数的名称应为countApples

levelWithMaxApples Empty = 0
levelWithMaxApples (Node Apple l r) = 1 + levelWithMaxApple l + levelWithMaxApple r
levelWithMaxApples (Node Peach l r) = levelWithMaxApple l +   levelWithMaxApple r

感谢您的帮助!

2 个答案:

答案 0 :(得分:2)

这个答案是literate Haskell。您可以使用.lhs扩展名对其进行保存,然后将其加载到GHCi中。

> import Data.Ord (comparing)
> import Data.List (maximumBy)

> data Fruit = Peach | Apple
> data BTree a = Empty | Node a (BTree a) (BTree a)

尝试将其分解为更小的部分。首先,编写一个函数,生成树中所有级别的列表:

> levels :: BTree a -> [[a]]
> levels Empty = []
> levels (Node x l r) = [x] : combine (levels l) (levels r)
>   where combine [] ys = ys
>         combine xs [] = xs
>         combine (x:xs) (y:ys) = (x ++ y) : combine xs ys

(请注意,此处的combine辅助函数与zipWith (++)类似,但在最短输入用尽后继续。

完成后,您可以很容易地在列表中找到Apple的实例(如果您将deriving Eq添加到Fruit定义,则会更容易:

> countEach :: (a -> Bool) -> [[a]] -> [Int]
> countEach pred = map (length . filter pred)

> countApples :: [[Fruit]] -> [Int]
> countApples = countEach isApple
>   where isApple Apple = True
>         isApple _ = False

接下来,您只需使用zip标记列表中的每个项目及其索引编号,然后使用maximumBy选择计数最多的项目:

> levelWithMaxApples :: BTree Fruit -> Int
> levelWithMaxApples t = let ls = levels t
>                            counts = countApples ls
>                            labeled = zip [0..] counts
>                        in fst . maximumBy (comparing snd) $ labeled

答案 1 :(得分:1)

这是另一种方式。

  1. 从辅助函数开始。这将简单地构建节点的原始列表以及该节点处的Apple计数。但是,对每个子树中的每个节点分别进行此操作,即列表将类似于[(Root,1),(L1,1),(L2,0),(R1,1)]

    import qualified Data.Map as M (fromListWith,toList)
    import qualified Data.List as L (sortBy)
    
    countApples' :: BTree Fruit -> Int -> [(Int,Int)]
    countApples' Empty _ = []
    countApples' (Node Apple l r) n = (n,1) : (countApples' l (n+1)) ++ (countApples' r (n+1))
    countApples' (Node Peach l r) n = (n,0) : (countApples' l (n+1)) ++ (countApples' r (n+1))
    
  2. 接下来,我们使用上面的步骤创建一个Map。在此步骤中,我们将聚合树的每个级别的值。然后我们转换回列表并按元组的值部分按降序排序。具有最大apple实例的级别将成为列表头部元组中的第一个元素。

    levelWithMaxApples  :: BTree Fruit -> Int
    levelWithMaxApples  Empty = error "Empty tree"
    levelWithMaxApples  x = fst $ head $ L.sortBy (\(k1,v1) (k2,v2) -> compare v2 v1) $ M.toList $ M.fromListWith (+) $ countApples' x 0
    
  3. Demo

    但是,您应该注意,这可能不是一个非常有效的解决方案,因为除了排序之外还要转换为Map。

    注意:排序部分主要基于this回答。

    更新:以下是使用Google地图完成此操作的方法。

    import qualified Data.Map as M
    
    countApples :: BTree Fruit -> Int -> M.Map Int Int
    countApples Empty _ = M.empty
    countApples (Node Apple l r) n = M.unionsWith (+) [(M.singleton n 1 ),(countApples l (n+1)),(countApples r (n+1))] 
    countApples (Node Peach l r) n = M.unionsWith (+) [(M.singleton n 0 ),(countApples l (n+1)),(countApples r (n+1))]
    
    levelWithMaxApples  :: BTree Fruit -> Int
    levelWithMaxApples t = fst $ M.foldWithKey (\k v acc@(k',v') -> if v >= v' then (k,v) else acc) (-1,-1) $ countApples t 0
    

    Demo