Rechercher si un arbre est un arbre de recherche binaire dans 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)

Ce que je veux faire est d'écrire une fonction pour déterminer si l'arbre donné est un arbre de recherche binaire, ma méthode est de regrouper toutes les valeurs dans une liste et d'importer Data.Listpuis de trier la liste pour savoir si elles sont égales, mais cela est un peu compliqué. Pouvons-nous le faire sans importer d'autre module?

Jayyyyyy
la source
Je ne définirais pas en flattenTreepremier. Vous pouvez revenir Falsetôt si un nœud viole la propriété de recherche sans avoir à parcourir la totalité du sous-arbre enraciné sur ce nœud.
chepner
@chepner le problème est avec sort, pas avec flattenTree, ce qui est assez paresseux.
Will Ness
Oui, cela m'est venu à l'esprit après avoir regardé certaines des autres réponses.
chepner

Réponses:

13

Voici une façon de le faire sans aplatir l'arbre.

De la définition, ici,

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

on peut voir que traverser l'arbre de gauche à droite, en ignorant Nodeet entre parenthèses, vous donne une séquence alternée de Nulls et as. Autrement dit, entre toutes les deux valeurs, il y a un Null.

Mon plan est de vérifier que chaque sous-arbre satisfait aux exigences appropriées : nous pouvons affiner les exigences à chacune Node, en nous souvenant des valeurs entre lesquelles nous nous trouvons, puis les tester à chacune Null. Comme il y a Nullentre chaque paire de valeurs dans l'ordre, nous aurons testé que toutes les paires dans l'ordre (de gauche à droite) ne sont pas décroissantes.

Qu'est-ce qu'une exigence? C'est une limite inférieure et supérieure lâche sur les valeurs de l'arbre. Pour exprimer les exigences, y compris celles situées aux extrémités gauche et droite, nous pouvons étendre toute commande avec Bottom et Topéléments, comme suit:

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

Vérifions maintenant qu'un arbre donné satisfait aux exigences d'être à la fois en ordre et entre des bornes données.

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

Un arbre de recherche binaire est un arbre qui est dans l'ordre et entre Botet Top.

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

Le calcul des valeurs extrêmes réelles dans chaque sous-arbre, leur propagation vers l'extérieur, vous donne plus d'informations que nécessaire, et est fastidieux dans les cas extrêmes où un sous-arbre gauche ou droit est vide. Maintenir et vérifier les exigences , les pousser vers l'intérieur, est plutôt plus uniforme.

travailleur du porc
la source
6

Voici un indice: créer une fonction auxiliaire

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

BSTResult aest défini comme

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

Vous devriez pouvoir procéder de manière récursive, en exploitant les résultats sur les sous-arbres pour piloter le calcul, en particulier le minimum et le maximum.

Par exemple, si vous avez tree = Node left 20 right, avec isBSTree' left = NonEmptyBST 1 14et isBSTree' right = NonEmptyBST 21 45, alors isBSTree' treedevrait l'être NonEmptyBST 1 45.

Dans le même cas, sauf pour tree = Node left 24 right, nous devrions plutôt avoir isBSTree' tree = NotBST.

La conversion du résultat en Boolest alors triviale.

chi
la source
1
ou définissez le monoïde évident BSTResult aet pliez-le. :) (ou même si ce n'est pas un Monoïde légal ....)
Will Ness
(mais c'est légal, de toute façon, je pense)
Will Ness
3

Oui , vous n'avez pas besoin de trier la liste. Vous pouvez vérifier si chaque élément est inférieur ou égal à l'élément suivant. Ceci est plus efficace puisque nous pouvons le faire dans O (n) , alors que l'évaluation de la liste triée prend complètement O (n log n) .

On peut donc vérifier cela avec:

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

On peut donc vérifier si l'arbre binaire est un arbre de recherche binaire avec:

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

Je pense que l'on peut affirmer que Nulllui - même est un arbre de recherche binaire, car c'est un arbre vide. Cela signifie donc que pour chaque nœud (il n'y a pas de nœuds), les éléments du sous-arbre gauche sont inférieurs ou égaux à la valeur du nœud, et les éléments du sous-arbre droit sont tous supérieurs ou égaux à la valeur du nœud .

Willem Van Onsem
la source
1

Nous pouvons procéder de gauche à droite sur l'arbre comme ceci:

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

Inspiré par John McCarthy'sgopher .

La liste déroulante explicite peut être éliminée avec la continuation-passage,

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)

Le maintien d'un seul élément, le plus grand à ce jour , suffit.

Will Ness
la source