【问题标题】:Build balanced binary tree with foldr用 foldr 构建平衡二叉树
【发布时间】:2013-04-15 22:49:03
【问题描述】:

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

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

是否可以在没有递归的情况下编写insertInTree(带有until/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> 

【问题讨论】:

  • 你认为树的高度是什么意思?你能定义它吗?这与 insertInTree 的计算结果相匹配吗?
  • 我的家庭作业中只有这个定义:二叉树的高度是从根到最深节点的路径长度。例如,具有单个节点的树的高度为 0;具有三个节点且根有两个孩子的树的高度为 1;等等。哦!这个高度计算有问题=((
  • 任务是从已排序的列表中创建树吗?您的递归 insertInTree 很好。你可以制作foldTree = foldr insertInTree Leaf。除了代码审查类型的东西,你能澄清一下你在问什么吗?
  • 我现在不确定 insertTree 函数:计算高度是否正确?我的意思是节点内(h2+1)节点(h1+1)?以及如何将 insertTree 作为一对管道函数?
  • 计算高度对于平衡来说是必要的。您已经保存了每个节点的高度,所以 heightTree 是 O(1)。

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


【解决方案1】:

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

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

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

这会创建几乎平衡的树(也称为 AVL 树)。但是它将每个新元素推到树的最底部。

编辑:这些树可以很好地看到

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

【讨论】:

    【解决方案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
    

    【讨论】:

    • 不,接受的答案考虑了所有可能性,并按照宣传的方式工作。它创建树,使得对于树中的每个节点,节点的两个分支的深度最多相差 1,也就是 AVL 树。我能找到的深度超过 3 没有问题。我已经使用新功能编辑了答案,以类似树的可视化方式显示这些树,并建议进行测试。
    • 刚刚用你的函数试了一下;对于我的答案中显示的测试用例,我的函数创建深度为 7 的树;你的确实创造了更平衡的树,深度为 6;以拥有更复杂的代码为代价。
    猜你喜欢
    • 2023-03-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-09-13
    • 2018-08-30
    • 1970-01-01
    • 1970-01-01
    • 2015-01-08
    相关资源
    最近更新 更多