Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/haskell/8.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Algorithm 我应该如何在Haskell中定义二叉树?_Algorithm_Haskell_Data Structures_Tree - Fatal编程技术网

Algorithm 我应该如何在Haskell中定义二叉树?

Algorithm 我应该如何在Haskell中定义二叉树?,algorithm,haskell,data-structures,tree,Algorithm,Haskell,Data Structures,Tree,在Haskell中,二叉树可以通过以下两种方式之一定义: data Tree a = Empty | Branch a (Tree a) (Tree a) 或 选择一个比另一个有什么好处?在哪种情况下,一种树结构比另一种更合适?前者肯定更好,因为它可以表示任意二叉树,后者则不是这样。例如,第二个版本不能表示: 一棵空树 有一个节点的树,该节点有一个左边的子节点,但没有右边的子节点(反之亦然) 这在很大程度上取决于您的应用程序。如果树的形状由元素决定,则前一个定义更好,例如,如果您有一个平衡二叉

在Haskell中,二叉树可以通过以下两种方式之一定义:

data Tree a = Empty | Branch a (Tree a) (Tree a)


选择一个比另一个有什么好处?在哪种情况下,一种树结构比另一种更合适?

前者肯定更好,因为它可以表示任意二叉树,后者则不是这样。例如,第二个版本不能表示:

  • 一棵空树

  • 有一个节点的树,该节点有一个左边的子节点,但没有右边的子节点(反之亦然)


  • 这在很大程度上取决于您的应用程序。如果树的形状由元素决定,则前一个定义更好,例如,如果您有一个平衡二叉树:

    另一方面,如果树充当无约束元素的容器,而树的形状不依赖于这些元素,则将值放在叶子上更有意义

    海因里希·阿普费尔摩斯(Heinrich Apfelmus)很好地展示了这种方法。他定义

    data Tree v a = Leaf   v a
                  | Branch v (Tree v a) (Tree v a)
    

    因此,类型
    a
    的值仅在叶上,但所有节点(内部和叶)都由类型
    v
    注释,只需为
    v
    选择不同的值,我们就会得到不同的有趣数据结构。

    正如@PetrPudlák所说,这取决于类型。前者更适合于搜索树。但是,后一个版本是(免费)monad,它也很有用:

    instance Monad Tree where
        return = Leaf
        Leaf x >>= f = f x
        Branch t1 t2 >>= f = Branch (t1 >>= f) (t2 >>= f)
    
    (>>=)
    运算符对应于“叶处的替换”

    Functor
    Applicative
    实例也很有用。 随着GHC 7.10的推出,当您定义
    Monad
    时,它们已成为必需的。我们可以使用monad函数来定义它们:

    instance Functor Tree where fmap = Control.Monad.liftM
    instance Applicative Tree where pure = return; (<*>) = Control.Monad.ap
    
    实例函子树,其中fmap=Control.Monad.liftM
    实例应用程序树,其中pure=return;()=Control.Monad.ap
    
    >P>我认为后一个几乎不有用,因为它可以被夷平为(非空)列表,唯一的区别是分支结构。但是,由于内部节点没有携带额外的信息,因此在运行时仍然很难对其进行分析;当你看到一个分支时,你不能对它的两个子树中的任何一个子树说任何话。为了区别它们,您必须遍历它们,并从本质上消除我们更喜欢树的O(logn)复杂性


    如果有人发现这种数据结构的用例,请告诉我。

    我可以理解,类型可以被视为“绝对更好”,因为它允许表示更多的用例,但更灵活并不总是等同于“更好”。我的意思是——通过同样的论点,人们可以说玫瑰树比二叉树更好,因为它们可以代表更多的树。@chi二叉树在问题中被明确提到(如果没有明确提到,那将是另一个问题)。二叉树是一个定义良好的概念,后一个版本不允许表示二叉树。并非在所有情况下都更好。一些数据结构被实现为树,树的叶子中只有数据。例如,第二个版本更好地表示哈夫曼代码树。另外,您提到的两种情况都可以用第二棵树来表示,使用
    树(可能是a)
    (最终将与第一棵树同构)。哎呀,实际上这不是同构的。不过,我支持我的另一点。
    数据树a=Leaf a |分支(树a)a(树a)
    应该也能正常工作,对吗?
    instance Functor Tree where fmap = Control.Monad.liftM
    instance Applicative Tree where pure = return; (<*>) = Control.Monad.ap