如何检查BST是否有效?

时间:2011-02-12 22:22:48

标签: haskell binary-search-tree fold

如果BST定义并使用BST的广义折叠版本,如何检查BST是否有效?

data(Ord a, Show a, Read  a) => BST a = Void | Node {
    val :: a,
    left, right :: BST a
} deriving (Eq,  Ord,  Read, Show)


fold :: (Read a, Show a, Ord a) => (a -> b -> b ->  b) -> b -> BST a -> b
fold _ z Void         = z
fold f z (Node x l r) = f x (fold f z l) (fold f z r)

想法是检查节点值是否大于左子树中的所有值,并且小于其右子树中的所有值。对于树中的所有节点,此值必须为True。函数bstList只输出BST中的(有序)值列表。

当然这样的事情不会起作用:

--isBST :: (Read a, Show a, Ord a) => BST a -> Bool
isBST t = fold (\x l r -> all (<x) (bstList l) && all (>x) (bstList r)) (True) t

因为,例如,将折叠函数应用于节点19最终会all (<19) (bstList True) && all (>19) (bstList True)

a BST

4 个答案:

答案 0 :(得分:4)

你的问题似乎是你丢失了信息,因为你的函数在检查左右子树时只返回一个布尔值。所以更改它也会返回子树的最小值和最大值。(这可能也更有效,因为您不需要使用bslist来检查所有元素了)

当你完成后,使包装函数忽略这些“辅助”值。

答案 1 :(得分:4)

(请不要在data类型上添加类型类约束。)

如果有序遍历单调增加,则BST有效。

flatten tree = fold (\a l r -> l . (a:) . r) id tree []

ordered list@(_:rest) = and $ zipWith (<) list rest
ordered _ = True

isBST = ordered . flatten

答案 2 :(得分:2)

一种很好的编码方式是依靠Data.Foldable提供的遍历。

{-# LANGUAGE DeriveFunctor, DeriveFoldable #-}
import Data.Foldable
import Data.Monoid

我们可以使用扩展自动派生它的实例,但我们需要重新排序Node构造函数的字段,以便为我们提供有序遍历。

虽然我们正在努力,但我们应该消除对数据类型本身的限制。它们实际上没有任何好处,并且从Haskell 2011开始就已经从语言中删除了。(当你想要使用这些约束时,你应该将它们放在类的实例上,而不是放在数据类型上。)

data BST a 
  = Void
  | Node
    { left :: BST a
    , val :: a
    , right :: BST a 
    } deriving (Eq, Ord, Read, Show, Foldable)

首先,我们定义列表严格排序的含义。

sorted :: Ord a => [a] -> Bool
sorted [] = True
sorted [x] = True
sorted (x:xs) = x < head xs && sorted xs 
-- head is safe because of the preceeding match.

然后我们可以使用toList提供的Data.Foldable方法和上面的帮助程序。

isBST :: Ord a => BST a -> Bool
isBST = sorted . toList

我们也可以像你问的那样更直接地实现这一点。由于我们删除了对数据类型的虚假约束,因此我们可以简化折叠的定义。

cata :: (b -> a -> b -> b) -> b -> BST a -> b
cata _ z Void         = z
cata f z (Node l x r) = f (cata f z l) x (cata f z r)

现在我们需要一种数据类型来模拟我们的catamorphism的结果,即我们要么没有节点(Z),要么有一系列严格增加的节点(T)或者失败了(X

data T a = Z | T a a | X deriving Eq

然后我们可以直接实施isBST

isBST' :: Ord a => BST a -> Bool
isBST' b = cata phi Z b /= X where
  phi X _ _ = X
  phi _ _ X = X
  phi Z a Z = T a a
  phi Z a (T b c) = if a < b then T a c else X
  phi (T a b) c Z = if b < c then T a c else X
  phi (T a b) c (T d e) = if b < c && c < d then T a e else X

这有点单调乏味,所以也许最好分解一下我们构建临时状态的方式:

cons :: Ord a => a -> T a -> T a
cons _ X = X
cons a Z = T a a
cons a (T b c) = if a < b then T a c else X

instance Ord a => Monoid (T a) where
  mempty = Z
  Z `mappend` a = a
  a `mappend` Z = a
  X `mappend` _ = X
  _ `mappend` X = X
  T a b `mappend` T c d = if b < c then T a d else X

isBST'' :: Ord a => BST a -> Bool
isBST'' b = cata phi Z b /= X where
  phi l a r = l `mappend` cons a r

就个人而言,我可能只是使用Foldable实例。

答案 3 :(得分:0)

如果您不坚持使用折叠,您可以这样做:

ord Void = True
ord (Node v l r) = every (< v) l && every (> v) r && ord l && ord r where
    every p Void = True
    every p (Node v l r) = p v && every p l && every p r