在Haskell中查找树是否为二叉搜索树
我想做的是编写一个函数来确定给定的树是否是一个二元搜索树,我的方法是将列表中的所有值分组并导入在Haskell中查找树是否为二叉搜索树,haskell,tree,binary-tree,binary-search-tree,predicate,Haskell,Tree,Binary Tree,Binary Search Tree,Predicate,我想做的是编写一个函数来确定给定的树是否是一个二元搜索树,我的方法是将列表中的所有值分组并导入Data.list,然后对列表进行排序以确定它们是否相等,但这有点复杂。我们可以在不导入其他模块的情况下执行此操作吗?是,您不需要对列表进行排序。您可以检查每个元素是否小于或等于下一个元素。这是更有效的,因为我们可以在O(n)中执行此操作,而计算排序列表完全需要O(n log n) 因此,我们可以通过以下方式进行检查: type BSTree a = BinaryTree a data Bin
Data.list
,然后对列表进行排序以确定它们是否相等,但这有点复杂。我们可以在不导入其他模块的情况下执行此操作吗?是,您不需要对列表进行排序。您可以检查每个元素是否小于或等于下一个元素。这是更有效的,因为我们可以在O(n)中执行此操作,而计算排序列表完全需要O(n log n)
因此,我们可以通过以下方式进行检查:
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)
我认为可以断言
Null
本身就是一个二叉搜索树,因为它是一个空树。因此,这意味着对于每个节点(没有节点),左子树中的元素小于或等于节点中的值,右子树中的元素都大于或等于节点中的值。这里有一个提示:生成一个辅助函数
isBSTree :: Ord a => BinaryTree a -> Bool
isBSTree = ordered . flattenTree
其中,BSTResult a
定义为
isBSTree' :: (Ord a) => BinaryTree a -> BSTResult a
您应该能够递归地进行,利用子树上的结果来驱动计算,特别是最小值和最大值
例如,如果您有tree=Node left 20 right
,带有isBSTree'left=NonEmptyBST 114
和isBSTree'right=NonEmptyBST 21 45
,那么isBSTree'树
应该是NonEmptyBST 1 45
在相同的情况下,除了tree=Node left 24 right
,我们应该使用isBSTree'tree=NotBST
因此,将结果转换为Bool非常简单。这里有一种方法可以在不压扁树的情况下完成 从这里的定义来看
data BSTResult a
= NotBST -- not a BST
| EmptyBST -- empty tree (hence a BST)
| NonEmptyBST a a -- nonempty BST with provided minimum and maximum
可以看到,从左到右遍历树,忽略节点
和括号,会得到Null
s和a
s的交替序列。也就是说,每两个值之间都有一个Null
我的计划是检查每个子树是否满足合适的需求:我们可以在每个节点
上细化需求,记住我们之间的值,然后在每个Null
上测试它们。由于每个顺序对值之间都有一个Null
,我们将测试所有顺序对(从左到右)都是非递减的
什么是要求?它是树中值的松散上下限。为了表达需求,包括最左端和最右端的需求,我们可以使用Bot
tom和Top
元素扩展任何订单,如下所示:
data BinaryTree a = Null | Node (BinaryTree a) a (BinaryTree a)
deriving Show
现在让我们检查一棵给定的树是否同时满足有序性和给定边界之间的要求
data TopBot a = Bot | Val a | Top deriving (Show, Eq, Ord)
计算每个子树中的实际极值,将它们向外冒泡,可以提供比所需更多的信息,并且在左子树或右子树为空的边缘情况下非常方便。维护和检查需求,将它们向内推,是相当统一的。我们可以在树上从左到右进行操作,如下所示:
isBSTree :: Ord a => BinaryTree a -> Bool
isBSTree = ordBetween Bot Top
只维护一个到目前为止最大的元素就足够了。我不会首先定义
flattree
。如果某个节点违反了搜索属性,则可以提前返回False
,而不必遍历该节点上根目录下的整个子树。@chepner问题在于排序
,而不是扁平树
,这已经够懒了。是的,在看了其他一些答案后,我想到了这一点。或者为BSTResult a
定义明显的幺半群并将其折叠起来:(或者,即使它不是一个合法的幺半群……)(但无论如何,我认为它是合法的)(只是一个建议,)你可以标记这个答案(或任何其他答案),并要求版主将你的旧帐户合并到这个帐户中,这样人们也可以从那里看到其他两个答案。(我试着要求mods这样做,但他们说只有用户自己可以请求合并帐户。):)(希望我在这里给你打电话不会太麻烦)为什么要等到Null来比较边界?对我来说,在递归之前,在每个节点上检查值是否在已经建立的边界内似乎更自然。此调整应该更慢:它可以正确地为树节点(节点未定义10未定义)5 Null生成False
。
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
isBSTree :: Ord a => BinaryTree a -> Bool
isBSTree = ordBetween Bot Top
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)