【问题标题】:Test if two trees are equal and binary search trees in Haskell在 Haskell 中测试两棵树是否相等和二叉搜索树
【发布时间】:2017-06-27 14:04:07
【问题描述】:

我有点坚持为二叉搜索树实现 Eq 实例。我想检查两棵树中的值是否相同,但我真的不知道如何正确地做到这一点。由于两个二叉搜索树的结构可能不同,我不能直接比较节点。我的想法是将它们转换成一个列表,对列表进行排序并比较它们,但这看起来很复杂,我想知道是否有另一种方法可以做到这一点。这是我到目前为止得到的:

data BB a = L | K a (BB a) (BB a) deriving (Show)

instance Eq BB where
  not (isBinarySearchTree t1 && isBinarySearchTree t2) = False
  inOrder t1 == inOrder t2 = True

【问题讨论】:

  • 如果这些是二叉搜索树,通常会得到一个排序列表。
  • 我也有同样的想法并尝试过(编辑了代码),但它不起作用。当然实现了 inOrder 和 isBinarySearchTree。
  • 但是not (isBinarySearchTree t1 && isBinarySearchTree t2) = False 行很奇怪:Eq 定义了两个函数:(==)(/=)
  • 我不知道如何使用相等运算符检查它是否是 bst,这就是我这样做的原因。我想这不是错误吗?
  • @Vajk 这个想法是您应该已经知道这些树是 BST,因为您将只允许保持 BST 要求的插入。您不必对其进行测试。

标签: haskell tree binary-search-tree equality


【解决方案1】:

接受的答案在我看来是错误的,因为您说要根据树的概念内容而不是它们的确切结构进行比较。也就是说,您希望以下内容为真:

K 1 L (K 2 L L) == K 2 (K 1 L L) L

两个 BST 具有相同的两个元素,顺序正确但结构不同。在结构上比较它们,如 severij 的回答,产生False,但你希望它是True

那么,如何按内容而不是按结构进行比较?好吧,任何两个具有相同项目集的 BST 将具有相同的中序遍历。因此,您可以完全按照您在问题中所说的那样做:将它们转换为列表,然后比较列表。但是您不必对它们进行排序:因为它们是 BST,所以可以保证您可以以相同的顺序遍历它们。并且将它们转换为列表并不是非常昂贵,所以不用担心:懒惰和流融合使其成本与手动编写遍历大致相同。

如果您的树类型有一个可折叠的实例,这将容易得多,这对树来说无论如何都是一件好事。定义Foldable只需要定义foldr,很简单:

import Prelude hiding (foldr)
import Data.Foldable (Foldable, foldr, toList)

instance Foldable BB where
  foldr f init L = init
  foldr f init (K x left right) = foldr f (f x (foldr f init right)) left

然后您可以根据 toList 定义 Eq:

instance Eq (BB a) where
  x == y = toList x == toList y

之后,您可以根据需要,

*Main> (K 1 L (K 2 L L)) == (K 2 (K 1 L L) L)
True

但这是相当多的工作,不是吗?在写这个答案时,我多次错误地定义了foldr。事实证明,有一种方法可以避免自己编写:DeriveFoldable 语言扩展让 GHC 为您派生 Foldable 就像派生 Show 一样容易。但是由于它会按顺序折叠数据类型中的字段,因此您必须对其进行一些更改,以便左子树出现在根节点之前:

{-# LANGUAGE DeriveFoldable #-}

import Prelude hiding (foldr)
import Data.Foldable (Foldable, foldr, toList)

data BB a = L | K (BB a) a (BB a) deriving (Show, Foldable)

instance Eq (BB a) where
  x == y = toList x == toList y

事实上,我们仍然拥有所需的相等属性(尽管我们必须以不同的方式编写节点,因为我们更改了字段顺序):

*Main> (K L 1 (K L 2 L)) == (K (K L 1 L) 2 L)
True

还有最后一项改进:如果您导入 Data.Function.on,则 Eq 定义也可以更简单。而不是手动拼写出来,on 你可以简单地说“比较两棵树是否相等,将每个树转换为一个列表并比较这些列表是否相等”。保持树定义和其他导入不变,我们终于获得了所需特征的非常简洁的定义,所有实际工作都由已经编写的库函数完成:

{-# LANGUAGE DeriveFoldable #-}

import Prelude hiding (foldr)
import Data.Foldable (Foldable, foldr, toList)
import Data.Function (on)

data BB a = L | K (BB a) a (BB a) deriving (Show, Foldable)

instance Eq a => Eq (BB a) where
  (==) = (==) `on` toList

【讨论】:

  • 流融合不用于 GHC 中的列表。折叠/构建融合用于那些。 Fold/build 可用于将相等性测试与其参数中的 one 融合,但我未能使其表现良好,因此它根本没有融合。也就是说,我仍然不知道有比您提供的解决方案更好的解决方案。
  • 我自己解决了这个问题,这就是我接受的原因,这样人们就可以看到不需要进一步的帮助,但感谢您的回答。
  • @Vajk 感谢您为避免浪费他人时间所做的努力。但实际上,Stack Overflow 不仅可以帮助通过 Google 找到问题的未来访问者,还可以帮助最初提出问题的人。因此,最好只接受一个对问题的好答案的答案:很高兴您找到了另一个解决方案,您甚至可以回答自己的问题来解释解决方案,但不要仅仅接受错误的答案表示您不再需要帮助。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-03-24
  • 2013-07-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-08-01
相关资源
最近更新 更多