Come posso verificare se un BST è valida?

voti
6

Come posso verificare se un BST è uno valido, data la sua definizione e l'utilizzo di una versione generalizzata di volte per 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)

L' idea è quella di verificare che un valore di nodo è maggiore di tutti i valori a sinistra-sottostruttura e più piccolo di tutti i valori nella sua destro sottostruttura. Questo deve essere Trueper tutti i nodi dell'albero. Una funzione bstListsemplicemente produrre l'elenco di valori (ordinate) del BST.

Naturalmente qualcosa come questo non funziona:

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

perché, per esempio, applicando la funzione di piegatura al nodo 19finisce all (<19) (bstList True) && all (>19) (bstList True).

un

È pubblicato 12/02/2011 alle 23:22
fonte dall'utente
In altre lingue...                            


4 risposte

voti
4

Il tuo problema sembra essere che si perde informazioni perché la funzione restituisce solo un valore booleano quando esamina le sottostrutture sinistro e destro. Quindi cambiare per restituire anche i valori minimi e massimi delle sottostrutture. (Questo è probabilmente più efficiente e, dal momento che non c'è bisogno di utilizzare bslistper controllare tutti gli elementi più)

E fa una funzione wrapper per ignorare questi valori "ausiliari" dopo aver terminato, naturalmente.

Risposto il 12/02/2011 a 23:38
fonte dall'utente

voti
4

(Si prega di non mettere vincoli typeclass sul datatipo.)

Un BST è valida se e solo se un in-ordine di attraversamento è monotonicamente crescente.

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

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

isBST = ordered . flatten
Risposto il 13/02/2011 a 05:53
fonte dall'utente

voti
0

Se non si ostini a usare una piega si può fare in questo modo:

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
Risposto il 13/02/2011 a 07:45
fonte dall'utente

voti
2

Un bel modo di codificare questo è quello di appoggiarsi al attraversamento fornito da Data.Foldable.

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

Siamo in grado di ricavare un'istanza di esso automaticamente utilizzando un'estensione, ma abbiamo bisogno di riordinare i campi del costruttore nodo di fornirci un attraversamento in ordine.

Già che ci siamo, dovremmo eliminare i vincoli sul tipo di dati stessi. Essi in realtà non forniscono alcun beneficio, ed è stato rimosso dal linguaggio come di Haskell 2011. (Quando si desidera utilizzare tali vincoli li si dovrebbe mettere su istanze di classi, non sul tipo di dati.)

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

In primo luogo definiamo che cosa significa per una lista per essere rigorosamente allineati.

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.

Poi possiamo utilizzare il toListmetodo previsto dal Data.Foldablee l'assistente sopra.

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

Possiamo anche implementare questo in modo più diretto, come hai chiesto. Poiché abbiamo rimosso i vincoli spurie sul tipo di dati, possiamo semplificare la definizione della piega.

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)

Ora abbiamo bisogno di un tipo di dati per modellare il risultato del nostro catamorphism, che è che noi non abbiamo né nodi ( Z), o di una serie di nodi strettamente crescente ( T) o sono falliti ( X)

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

E possiamo quindi implementare isBSTdirettamente

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

Questo è un po 'noiosa, quindi forse sarebbe meglio per decomporre il modo in cui componiamo gli stati intermedi un po':

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

Personalmente, mi piacerebbe probabilmente basta usare l'istanza pieghevole.

Risposto il 13/02/2011 a 16:31
fonte dall'utente

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