【问题标题】:Listing the paths to leaves in a tree列出树中叶子的路径
【发布时间】:2013-02-26 04:13:13
【问题描述】:

我正在尝试编写一个函数来查找树中叶子的所有路径。 例如,给定一棵如下所示的树:

           1
         /  \
        2    5
       / \    \
      3   4    6

输出列表将是:[[1,2,3],[1,2,4],[1,5,6]]

这个函数的类型签名是:branches :: Tree a -> [[a]]。请注意,这使用了 Data.Tree 包中定义的 Tree 类型,虽然示例树是二叉树,但实际的树类型是玫瑰树。

【问题讨论】:

标签: list haskell tree


【解决方案1】:

只是一个简单的函数:

listtree :: [a] -> Tree a -> [[a]]
listtree l (Node a []) = [l++[a]]
listtree l (Node a forest) = concatMap (listtree (l++[a])) forest

使用列表记录从根到当前节点的路径,并将当前节点的标签附加到路径上,然后递归地将listtree映射到每个子节点。

listtree [] (Node 1 [(Node 2 [(Node 3 []), (Node 4 [])]), (Node 5 [(Node 6 [])])]))

得到想要的结果[[1,2,3],[1,2,4],[1,5,6]]

【讨论】:

    【解决方案2】:

    我会通过在部分路径列表的前面添加新标签来构建路径,然后将它们反转以输出:

    listtree tree = map reverse $ traverse [] tree
      where traverse path (Node label []) = [label:path]
            traverse path (Node label xs) = concat $ map (traverse (label:path)) xs
    

    将标签添加到列表前面而不是末尾的原因是追加需要 O(N) 时间和分配内存,而添加头部需要 O(1)。当然,反转列表也是O(N),但是每个列表只反转一次……

    因此,在处理函数式算法和数据结构时,上述“添加到头部,然后在必要时反转”模式是一种普遍的习惯用法。


    编辑:来自@luqui 的评论,获取路径的一种补充方法是自下而上构建它们:

    listtree (Node label []) = [[label]]
    listtree (Node label xs) = map (label:) $ concat $ map listtree xs
    

    这比我的解决方案更短(并且可能更清晰),并且它还有一个额外的优势,即按照您想要的顺序为您提供路径:路径是从叶子开始构建的,而不是从根开始。

    注意(与之前的解决方案一样)路径列表是通过在列表的开头添加头部来扩展的,而不是附加到末尾。

    【讨论】:

    • 谢谢,这正是我需要做的。我自己的方法类似,但我完全忘记了 concat,因此该解决方案具有可变数量的嵌套列表,这让我困惑地摸不着头脑。
    • 尾递归在这里不是最理想的,因为你必须逆向并且它失去了共享。 paths (Node x []) = [[x]]; paths (Node x ts) = map (x:) (concatMap paths ts) 更适合懒惰和分享。 (可以通过使用无可辩驳的模式表示路径永远不会为空来进一步改进,然后您甚至可以在到达第一片叶子之前开始输出第一条路径)
    • @luqui,我喜欢这个作为补充解决方案(正如你提到的,从下到上构建路径会按照提问者想要的顺序提供给你)。但是,它支持什么样的共享? (我还要注意我的解决方案实际上并不是尾递归的......)
    • @comingstorm,啊,是的,我的意思是使用累加器,我将它与尾递归相关联,所以我不小心说了这个。但实际上,我认为我对分享的看法是错误的。我今天只是仓促的概括。无论如何,我假设直接版本的性能应该不比累加器版本差,但从我的准确率来看,我可能错了(或者我可能错了)
    • @luqui,我已经在我的回答中添加了一个包含您解决方案的部分。
    【解决方案3】:

    这很简单...这些是您需要牢记的事情:-

    1. 递归调用函数
    2. 在函数内,遍历的形式应该是 PreOrder(根、左、右)
    3. 在递归函数中使用数组并存储访问的每个节点的值。
    4. 如果访问的节点是叶子...节点--> 左== NULL 和节点--> 右== NULL。然后输出数组的内容。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-06-11
      • 2022-01-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-09-19
      • 2011-10-31
      相关资源
      最近更新 更多