在Haskell中,可以用以下两种方式之一定义二叉树:
data Tree a = Empty | Branch a (Tree a) (Tree a)
或
data Tree a = Leaf a | Branch (Tree a) (Tree a)
选择一个优于另一个有什么好处?在哪种情况下,一个树结构比另一个更好?
答案 0 :(得分:7)
这在很大程度上取决于您的申请。如果树的形状由元素决定,前一个定义会更好,例如,如果你有一个平衡的二叉树:
另一方面,如果您的树充当不受约束的元素的容器,其中树的形状不依赖于它们,则将值放到叶子上更有意义。
Heinrich Apfelmus的This post非常好地展示了这种方法。他定义了
data Tree v a = Leaf v a
| Branch v (Tree v a) (Tree v a)
因此类型a
的值仅在叶子上,但所有节点(内部和叶子)都按类型v
注释,只需为{{1}选择不同的monoids我们得到了不同的有趣数据结构。
答案 1 :(得分:5)
正如@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
答案 2 :(得分:2)
前者肯定更好,因为它可以代表任意二叉树,而后者则不然。例如,第二个版本不能代表:
空树。
一棵树,其节点具有左子节点而不是右子节点(反之亦然)。
答案 3 :(得分:0)
我认为后者几乎没用,因为它可以被展平为(非空)列表,唯一的区别是分支结构。然而,在运行时仍难以分析,因为内部节点不承载额外的信息;当你看到一个分支时,你不能对它的两个子树中的任何一个说什么。要区别对待它们,你必须遍历它们并基本上杀死我们更喜欢树的O( log n)复杂性。
如果有人找到这种数据结构的用例,请告诉我。