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