通过折叠查找二进制搜索树的所有叶

时间:2018-06-19 19:49:40

标签: haskell binary-tree fold

我的任务是从这种数据类型的二叉树中获取所有叶子:

data Bintree el = Empty |
                  Tree {left :: Bintree el, node :: el, right :: Bintree el} deriving Show

现在,我有了这个Treefold函数,可以在测试中完美运行:

binTreeFold :: (a -> b -> a -> a) -> a -> Bintree b -> a
binTreeFold f el Empty = el 
binTreeFold f el tree@(Tree leftTree node rightTree) = f (binTreeFold f el leftTree) node (binTreeFold f el rightTree)

我尝试获取所有叶子,但是以某种方式根本无法正常工作(下面提到的编译问题):

leavesWithFold :: Bintree a -> [a]
leavesWithFold Empty = []
leavesWithFold tree =  binTreeFold (\left -> \node -> \right -> if (checkIfTreeIsLeaf node) then left ++ [node] ++ right else left ++ [] ++ right) [] tree

使用以下功能:

checkIfTreeIsLeaf :: Bintree a -> Bool
checkIfTreeIsLeaf Empty = False
checkIfTreeIsLeaf (Tree Empty node Empty) = True
checkIfTreeIsLeaf _ = False

编译器告诉我

Couldn't match type `a' with `Bintree a0'

但是在以前的功能中,使用

left ++ [node] ++ right

工作正常。你知道我做错了什么,也许我可以改变什么?

最好的愿望

1 个答案:

答案 0 :(得分:2)

通常,使用fold函数的想法是它可以为您完成所有工作。因此,通过编写分支为Emptytree大小写的函数,这已经有点奇怪了。

一种递归方法

但是让我们简单地从递归方法开始。暂时不要折叠等。我们一般如何生成叶子列表?

这里基本上有三种情况:

  1. 如果遇到Empty,则返回空列表,因为Empty不是 一片叶子,因此应该返回所有叶子,所以:

    leaves Empty = []
    
  2. 如果我们遇到一个Tree作为左子树和右子树的Empty,我们知道Tree是叶子,因此我们返回它携带的元素,作为列表:

    leaves (Tree Empty x Empty) = [x]
    
  3. 如果两个子树之一(或两者都不是)Empty,我们将递归调用leaves函数,并将两个子树连接在一起:

    leaves (Tree l _ r) = leaves l ++ leaves r
    

所以现在我们得到了:

leaves :: Bintree a -> [a]
leaves Empty = []
leaves (Tree Empty x Empty) = [x]
leaves (Tree l x r) = leaves l ++ leaves r

如何通过递归检测到 是一片叶子

我们当然无法使用binTreeFold方法做到这一点。但是我们不能检测树木是否实际上是叶子吗?如果两个递归调用导致一个空列表,我们知道当前的Bintree实际上是一片叶子。确实:如果左(和右)子树为Empty,则它们将导致空列表。并且在这些不为空的情况下,它们要么是叶子,将递归地生成单例列表,要么不是带有后代的Tree,最终将导致叶子,因此导致非空列表。

fold 函数中对其进行编码

因此,我们首先看一下递归调用,然后基于以下事实:它们是否都是空列表,要么返回具有leaf元素的单例列表,要么将两个列表连接起来在一起,就像:

f [] x [] = [x]
f l _ r = l ++ r

或者在binTreeFold函数中使用它:

leavesWithFold :: Bintree a -> [a]
leavesWithFold = binTreeFold f []
    where f [] x [] = [x]
          f l _ r = l ++ r