¿Cómo puedo comprobar si un BST es válida?

votos
6

¿Cómo puedo comprobar si un BST es válida, dada su definición y el uso de una versión generalizada del pliegue de BST?

data(Ord a, Show a, Read  a) => BST a = Void | Node {
    val :: a,
    left, right :: BST a
} deriving (Eq,  Ord,  Read, Show)


fold :: (Read a, Show a, Ord a) => (a -> b -> b ->  b) -> b -> BST a -> b
fold _ z Void         = z
fold f z (Node x l r) = f x (fold f z l) (fold f z r)

La idea es comprobar que un valor de nodo es mayor que todos los valores de izquierda-subárbol y más pequeño que todos los valores en su derecho-subárbol. Esto debe ser Truepara todos los nodos en el árbol. Una función bstListsimplemente de salida la lista de valores (ordenadas) en el BST.

Por supuesto algo como esto no funcionará:

--isBST :: (Read a, Show a, Ord a) => BST a -> Bool
isBST t = fold (\x l r -> all (<x) (bstList l) && all (>x) (bstList r)) (True) t

porque, por ejemplo, la aplicación de la función de plegado al nodo 19termina all (<19) (bstList True) && all (>19) (bstList True).

un

Publicado el 12/02/2011 a las 23:22
fuente por usuario
En otros idiomas...                            


4 respuestas

votos
4

Su problema parece ser que se pierde información debido a que su función sólo devuelve un valor lógico cuando se examinan los subárboles izquierdo y derecho. Así que cambiarlo a devolver también los valores mínimos y máximos de los subárboles. (Esto es probablemente más eficiente, así, ya que no es necesario que se usa bslistpara comprobar todos los elementos más)

Y hacer una función de contenedor para ignorar estos valores "auxiliares" después de que haya terminado, por supuesto.

Respondida el 12/02/2011 a las 23:38
fuente por usuario

votos
4

(Por favor, no ponga restricciones clase de tipos en el datatipo.)

Un BST es válida si y sólo si una en orden transversal está aumentando de forma monótona.

flatten tree = fold (\a l r -> l . (a:) . r) id tree []

ordered list@(_:rest) = and $ zipWith (<) list rest
ordered _ = True

isBST = ordered . flatten
Respondida el 13/02/2011 a las 05:53
fuente por usuario

votos
0

Si no insiste en usar un pliegue puede hacerlo de esta manera:

ord Void = True
ord (Node v l r) = every (< v) l && every (> v) r && ord l && ord r where
    every p Void = True
    every p (Node v l r) = p v && every p l && every p r
Respondida el 13/02/2011 a las 07:45
fuente por usuario

votos
2

Una buena manera de codificar esto es para apoyarse en el recorrido proporcionada por Data.Foldable.

{-# LANGUAGE DeriveFunctor, DeriveFoldable #-}
import Data.Foldable
import Data.Monoid

Podemos obtener una instancia del mismo de forma automática utilizando una extensión, pero tenemos que cambiar el orden de los campos del nodo constructor para proporcionar un recorrido en orden.

Mientras estamos en ello, debemos eliminar las restricciones en el tipo de datos en sí. Que en realidad no proporcionan ningún beneficio, y se ha quitado de la lengua como de Haskell 2011. (Cuando se desea utilizar este tipo de limitaciones que debe ponerlos en instancias de clases, no en el tipo de datos.)

data BST a 
  = Void
  | Node
    { left :: BST a
    , val :: a
    , right :: BST a 
    } deriving (Eq, Ord, Read, Show, Foldable)

Primero definimos lo que significa para una lista a ser estrictamente ordenadas.

sorted :: Ord a => [a] -> Bool
sorted [] = True
sorted [x] = True
sorted (x:xs) = x < head xs && sorted xs 
-- head is safe because of the preceeding match.

Entonces podemos usar el toListmétodo proporcionado por Data.Foldabley el ayudante anteriormente.

isBST :: Ord a => BST a -> Bool
isBST = sorted . toList

También podemos poner en práctica esta forma más directa, como lo pediste. Puesto que hemos eliminado las restricciones espurias sobre el tipo de datos, podemos simplificar la definición de su redil.

cata :: (b -> a -> b -> b) -> b -> BST a -> b
cata _ z Void         = z
cata f z (Node l x r) = f (cata f z l) x (cata f z r)

Ahora necesitamos un tipo de datos para modelar el resultado de nuestra catamorphism, que es que o bien no tienen nodos ( Z), o un rango de nodos estrictamente creciente ( T) o han fallado ( X)

data T a = Z | T a a | X deriving Eq

Y entonces podemos aplicar isBSTdirectamente

isBST' :: Ord a => BST a -> Bool
isBST' b = cata phi Z b /= X where
  phi X _ _ = X
  phi _ _ X = X
  phi Z a Z = T a a
  phi Z a (T b c) = if a < b then T a c else X
  phi (T a b) c Z = if b < c then T a c else X
  phi (T a b) c (T d e) = if b < c && c < d then T a e else X

Esto es un poco tedioso, por lo que quizá sería mejor para descomponer la forma en que componemos los estados intermedios un poco:

cons :: Ord a => a -> T a -> T a
cons _ X = X
cons a Z = T a a
cons a (T b c) = if a < b then T a c else X

instance Ord a => Monoid (T a) where
  mempty = Z
  Z `mappend` a = a
  a `mappend` Z = a
  X `mappend` _ = X
  _ `mappend` X = X
  T a b `mappend` T c d = if b < c then T a d else X

isBST'' :: Ord a => BST a -> Bool
isBST'' b = cata phi Z b /= X where
  phi l a r = l `mappend` cons a r

En lo personal, yo probablemente sólo tiene que utilizar la instancia plegable.

Respondida el 13/02/2011 a las 16:31
fuente por usuario

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more