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