type BSTree a = BinaryTree a
data BinaryTree a = Null | Node (BinaryTree a) a (BinaryTree a)
deriving Show
flattenTree :: BinaryTree a -> [a]
flattenTree tree = case tree of
Null -> []
Node left val right -> (flattenTree left) ++ [val] ++ (flattenTree right)
isBSTree :: (Ord a) => BinaryTree a -> Bool
isBSTree btree = case btree of
Null -> False
tree -> (flattenTree tree) == sort (flattenTree tree)
我要做的是编写一个函数来确定给定的树是否是二叉搜索树,我的方法是将所有值分组到一个列表中,然后导入Data.List
,然后将列表排序为找出它们是否相等,但这有点复杂。我们可以在不导入其他模块的情况下做到这一点吗?
答案 0 :(得分:12)
这是一种无需压扁树木的方法。
从定义开始,在这里
data BinaryTree a = Null | Node (BinaryTree a) a (BinaryTree a)
deriving Show
可以看到,从左到右遍历该树,而忽略了Node
和括号,这给出了Null
和a
的交替序列。也就是说,每两个值之间有一个Null
。
我的计划是检查每个子树是否满足适当的要求:我们可以优化每个Node
的要求,记住我们之间的值,然后在每个Null
上测试。由于每个有序对值之间都有一个Null
,我们将测试所有有序(从左至右)对都不会减少。
有什么要求?这是树中值的松散上下限。为了表达需求,包括最左边和最右边的需求,我们可以使用Bot
tom和Top
元素扩展任何顺序,如下所示:
data TopBot a = Bot | Val a | Top deriving (Show, Eq, Ord)
现在让我们检查给定的树是否满足顺序和在给定范围之间的要求。
ordBetween :: Ord a => TopBot a -> TopBot a -> BinaryTree a -> Bool
-- tighten the demanded bounds, left and right of any Node
ordBetween lo hi (Node l x r) = ordBetween lo (Val x) l && ordBetween (Val x) hi r
-- check that the demanded bounds are in order when we reach Null
ordBetween lo hi Null = lo <= hi
二分搜索树是顺序在Bot
和Top
之间的树。
isBSTree :: Ord a => BinaryTree a -> Bool
isBSTree = ordBetween Bot Top
计算每个子树中的 actual 极值,将它们向外冒泡,会为您提供比您所需更多的信息,并且在左右子树为空的边缘情况下比较麻烦。维护和检查需求,将其向内推,则更加统一。
答案 1 :(得分:6)
这里有个提示:做一个辅助功能
isBSTree' :: (Ord a) => BinaryTree a -> BSTResult a
其中BSTResult a
被定义为
data BSTResult a
= NotBST -- not a BST
| EmptyBST -- empty tree (hence a BST)
| NonEmptyBST a a -- nonempty BST with provided minimum and maximum
您应该能够递归进行,利用子树上的结果来驱动计算,尤其是最小值和最大值。
例如,如果您有tree = Node left 20 right
,isBSTree' left = NonEmptyBST 1 14
和isBSTree' right = NonEmptyBST 21 45
,则isBSTree' tree
应该是NonEmptyBST 1 45
。
在相同情况下,除了tree = Node left 24 right
之外,我们应该改用isBSTree' tree = NotBST
。
将结果转换为Bool
很简单。
答案 2 :(得分:3)
是,您无需对列表进行排序。您可以检查每个元素是否小于或等于下一个元素。因为我们可以在 O(n)中执行此操作,所以效率更高,而完全评估排序的列表 则需要 O(n log n)。
因此,我们可以通过以下方式进行检查:
ordered :: Ord a => [a] -> Bool
ordered [] = True
ordered xa@(_:xs) = and (zipWith (<=) xa xs)
因此我们可以使用以下命令检查二叉树是否为二叉搜索树:
isBSTree :: Ord a => BinaryTree a -> Bool
isBSTree = ordered . flattenTree
我认为有人可以说Null
本身是二叉搜索树,因为它是一棵空树。因此,这意味着对于每个节点(没有节点),左子树中的元素都小于或等于该节点中的值,而右子树中的元素都大于或等于该节点中的值
答案 3 :(得分:1)
我们可以像这样从左到右在树上继续前进:
isBSTtreeG :: Ord a => BinaryTree a -> Bool
isBSTtreeG t = gopher Nothing [Right t]
where
gopher _ [] = True
gopher x (Right Null:ts) = gopher x ts
gopher x (Right (Node lt v rt):ts) = gopher x (Right lt:Left v:Right rt:ts)
gopher Nothing (Left v:ts) = gopher (Just v) ts
gopher (Just y) (Left v:ts) = y <= v && gopher (Just v) ts
通过继续传递可以消除显式下推列表,
isBSTtreeC :: Ord a => BinaryTree a -> Bool
isBSTtreeC t = gopher Nothing t (const True)
where
gopher x Null g = g x
gopher x (Node lt v rt) g = gopher x lt (\case
Nothing -> gopher (Just v) rt g
Just y -> y <= v && gopher (Just v) rt g)
仅维护一个迄今为止最大的元素就足够了。