使用foldr构建平衡二叉树

时间:2013-04-22 21:39:37

标签: haskell recursion binary-tree fold higher-order-functions

我编写了从列表构建平衡二叉树的函数foldTree。 我必须使用foldr并且没关系,我使用它,但我使insertInTree函数递归=(现在我只知道这种方式来遍历树=))。

UPDATE :我不确定函数insertTree:是否正确计算递归的高度? =((这里需要一些帮助。

是否可以在没有递归的情况下编写insertInTreeuntil/iterate/unfoldr的某些内容)或在没有辅助函数的情况下编写foldTree函数=>不知怎的更短?

这是我在下面的尝试:

data Tree a = Leaf
            | Node Integer (Tree a) a (Tree a)
            deriving (Show, Eq)

foldTree :: [a] -> Tree a
foldTree = foldr (\x tree -> insertInTree x tree) Leaf

insertInTree :: a -> Tree a -> Tree a
insertInTree x Leaf = Node 0 (Leaf) x (Leaf)
insertInTree x (Node n t1 val t2) = if h1 < h2 
                                    then Node (h2+1) (insertInTree x t1) val t2 
                                    else Node (h1+1) t1 val (insertInTree x t2) 
  where h1 = heightTree t1
        h2 = heightTree t2

heightTree :: Tree a -> Integer
heightTree Leaf = 0
heightTree (Node n t1 val t2) = n

输出:

*Main> foldTree "ABCDEFGHIJ"
Node 3 (Node 2 (Node 0 Leaf 'B' Leaf) 'G' (Node 1 Leaf 'F' (Node 0 Leaf 'C' Leaf))) 'J' (Node 2 (Node 1 Leaf 'D' (Node 0 Leaf 'A' Leaf)) 'I' (Node 1 Leaf 'H' (Node 0 Leaf 'E' Leaf)))
*Main> 

2 个答案:

答案 0 :(得分:4)

当两个子树的高度相等时,插入函数会出错,因为插入右子树会增加其高度(如果已经满了)。我不能立即清楚您的代码中是否会出现这种情况。

将新元素插入树中的明显正确方法似乎是

insertInTree x (Node n t1 val t2) 
    | h1 < h2   = Node  n (insertInTree x t1) val t2 
    | h1 > h2   = Node  n    t1 val t2n 
    | otherwise = Node (h+1) t1 val t2n  
  where h1  = heightTree t1
        h2  = heightTree t2
        t2n = insertInTree x t2
        h   = heightTree t2n     -- might stay the same

这会创建几乎平衡的树(a.k.a. AVL-trees)。但它将每个新元素推送到树的最底部。

编辑:使用

可以很好地看到这些树
showTree Leaf = ""  
showTree n@(Node i _ _ _) = go i n
  where
  go _ (Leaf) = "" 
  go i (Node _ l c r) = go (i-1) l ++ 
    replicate (4*fromIntegral i) ' ' ++ show c ++ "\n" ++ go (i-1) r 

尝试

  

putStr。 showTree $ foldTree“ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890”

是的,您可以将foldTree缩短为

foldTree = foldr insertInTree Leaf

答案 1 :(得分:2)

只是想指出接受的答案是好的,但是在拥有高度为3的平衡二叉树之后不会起作用,因为它没有考虑左插树在插入后的高度比右边少的事实。

显然,代码可能会增加额外的条件:

insertInTree x (Node n t1 val t2) 
    | h1 < h2   = Node  n      t1n val t2 
    | h1 > h2   = Node  n      t1  val t2n
    | nh1 < nh2 = Node  n      t1n val t2
    | otherwise = Node (nh2+1) t1  val t2n 
  where h1  = heightTree t1
        h2  = heightTree t2
        t1n = insertInTree x t1
        t2n = insertInTree x t2
        nh1 = heightTree t1n
        nh2 = heightTree t2n