Descubra se uma árvore é uma árvore de pesquisa binária em Haskell

10
  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)

O que eu quero fazer é escrever uma função para determinar se a árvore especificada é uma árvore de pesquisa binária, meu método é agrupar todos os valores em uma lista e importar Data.Liste, em seguida, classificar a lista para descobrir se são iguais, mas é um pouco complicado. Podemos fazer isso sem importar outro módulo?

Jayyyyyy
fonte
Eu não definiria flattenTreeprimeiro. Você pode retornar Falsecedo se um nó violar a propriedade de pesquisa sem precisar percorrer toda a subárvore enraizada nesse nó.
chepner
@chepner o problema é com sort, não com flattenTree, o que é preguiçoso o suficiente.
Will Ness
Sim, isso me ocorreu depois de examinar algumas das outras respostas.
Chepner # 9/19

Respostas:

13

Aqui está uma maneira de fazer isso sem achatar a árvore.

A partir da definição, aqui,

data BinaryTree a = Null | Node (BinaryTree a) a (BinaryTree a)
     deriving Show

pode-se ver que percorrer a árvore da esquerda para a direita, ignorando Nodee parênteses, fornece uma sequência alternada de Nulls e as. Ou seja, entre cada dois valores, existe um Null.

Meu plano é verificar se cada subárvore atende aos requisitos adequados : podemos refinar os requisitos em cada um Node, lembrando quais valores estamos entre eles e testá- los em cada um Null. Como existe um Nullpar de valores entre todos os em ordem, teremos testado que todos os pares em ordem (da esquerda para a direita) não diminuem.

O que é um requisito? É um limite inferior e superior solto nos valores da árvore. Para expressar requisitos, incluindo aqueles nas extremidades mais à esquerda e à direita, podemos estender qualquer pedido com Bottom e Topelementos, da seguinte maneira:

data TopBot a = Bot | Val a | Top deriving (Show, Eq, Ord)

Agora vamos verificar se uma determinada árvore atende aos requisitos de estar em ordem e entre limites determinados.

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

Uma árvore de pesquisa binária é uma árvore que está em ordem e entre Bote Top.

isBSTree :: Ord a => BinaryTree a -> Bool
isBSTree = ordBetween Bot Top

A computação dos valores extremais reais em cada subárvore, borbulhando para fora, fornece mais informações do que você precisa e é minuciosa nos casos extremos em que uma subárvore esquerda ou direita está vazia. Manter e verificar os requisitos , empurrando-os para dentro, é bastante mais uniforme.

trabalhador de porcos
fonte
6

Aqui está uma dica: crie uma função auxiliar

isBSTree' :: (Ord a) => BinaryTree a -> BSTResult a

onde BSTResult aé definido como

data BSTResult a
   = NotBST             -- not a BST
   | EmptyBST           -- empty tree (hence a BST)
   | NonEmptyBST a a    -- nonempty BST with provided minimum and maximum

Você deve poder recursivamente, explorando resultados em subárvores para conduzir o cálculo, em particular o mínimo e o máximo.

Por exemplo, se você tiver tree = Node left 20 right, com isBSTree' left = NonEmptyBST 1 14e isBSTree' right = NonEmptyBST 21 45, então isBSTree' treedeve ser NonEmptyBST 1 45.

No mesmo caso, exceto tree = Node left 24 right, deveríamos ter isBSTree' tree = NotBST.

Converter o resultado para Boolé então trivial.

chi
fonte
11
ou defina o óbvio Monoid BSTResult ae dobre nele. :) (ou mesmo se não é um Monoid legal ....)
Will Ness
(mas é lícito, de qualquer maneira, eu acho)
Will Ness
3

Sim , você não precisa classificar a lista. Você pode verificar se cada elemento é menor ou igual ao próximo elemento. Isso é mais eficiente, pois podemos fazer isso em O (n) , enquanto avaliar a lista classificada leva completamente O (n log n) .

Assim, podemos verificar isso com:

ordered :: Ord a => [a] -> Bool
ordered [] = True
ordered xa@(_:xs) = and (zipWith (<=) xa xs)

Portanto, podemos verificar se a árvore binária é uma árvore de pesquisa binária com:

isBSTree :: Ord a => BinaryTree a -> Bool
isBSTree = ordered . flattenTree

Penso que se pode afirmar que Nullé uma árvore de pesquisa binária, uma vez que é uma árvore vazia. Isso significa que, para cada nó (não há nós), os elementos na subárvore esquerda são menores ou iguais ao valor no nó, e os elementos na subárvore direita são todos maiores ou iguais ao valor no nó .

Willem Van Onsem
fonte
1

Podemos prosseguir da esquerda para a direita sobre a árvore desta maneira:

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

Inspirado por John McCarthygopher .

A lista push-down explícita pode ser eliminada com a passagem de continuação,

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)

Manter apenas um, o maior elemento até agora , é suficiente.

Will Ness
fonte