Вот способ сделать это без сплющивания дерева.
Из определения здесь
data BinaryTree a = Null | Node (BinaryTree a) a (BinaryTree a)
deriving Show
Можно видеть, что обход дерева слева направо, игнорирование Nodeи скобки дают чередующуюся последовательность Nulls и as. То есть между каждыми двумя значениями есть Null.
Мой план состоит в том, чтобы проверить, что каждое поддерево удовлетворяет подходящим требованиям : мы можем уточнить требования к каждому Node, помня, с какими значениями мы находимся, а затем протестировать их на каждом Null. Поскольку Nullмежду значениями в каждом порядке есть пара значений, мы проверим, что все в порядке (слева направо) пары не убывают.
Что такое требование? Это свободная нижняя и верхняя граница значений в дереве. Чтобы выразить требования, включая те, которые находятся слева и справа, мы можем расширить любой порядок с помощью Botтома и 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
Вычисление фактических экстремальных значений в каждом поддереве, всплытие их наружу, дает вам больше информации, чем вам нужно, и очень сложно в тех случаях, когда левое или правое поддерево пусто. Поддерживать и проверять требования , толкая их внутрь, довольно равномерно.
flattenTreeпервым. Вы можете вернутьсяFalseрано, если узел нарушает свойство поиска, не обходя все поддерево, укорененное в этом узле.