Hvordan kan jeg sjekke om en BST er gyldig?

stemmer
6

Hvordan kan jeg sjekke om en BST er gyldig, gitt sin definisjon og bruk av en generalisert versjon av ganger for 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)

Den ideen er å sjekke at en node verdien er større enn alle verdiene i venstre subtre og mindre enn alle verdier i sin høyre subtre. Dette må være Truefor alle noder i treet. En funksjon bstListbare skrive ut listen over (bestilt) verdier i BST.

Selvfølgelig noe sånt som dette ikke vil fungere:

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

fordi, for eksempel, å anvende falsfunksjonen til noden 19ender opp all (<19) (bstList True) && all (>19) (bstList True).

en

Publisert på 12/02/2011 klokken 22:22
kilden bruker
På andre språk...                            


4 svar

stemmer
4

Ditt problem synes å være at du mister informasjon fordi din funksjon bare returnerer en boolsk når den undersøker venstre og høyre subtre. Så endre det til også returnere minimums- og maksimumsverdier for subtreene. (Dette er sannsynligvis mer effektiv også, siden du ikke trenger å brukes bslisttil å sjekke alle elementer lenger)

Og lage en wrapper funksjon for å ignorere disse "hjelpe" verdier etter at du er ferdig, selvfølgelig.

Svarte 12/02/2011 kl. 22:38
kilden bruker

stemmer
4

(Vennligst ikke sette typeclass begrensninger på datatype.)

En BST er gyldig iff en in-order traversering er monotont økende.

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

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

isBST = ordered . flatten
Svarte 13/02/2011 kl. 04:53
kilden bruker

stemmer
0

Hvis du ikke insisterer på å bruke en fold kan du gjøre det slik:

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
Svarte 13/02/2011 kl. 06:45
kilden bruker

stemmer
2

En fin måte å kode dette på er å helle på traversering levert av Data.Foldable.

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

Vi kan utlede en forekomst av det automatisk ved hjelp av en utvidelse, men vi trenger å endre rekkefølgen på feltene i Node konstruktøren å gi oss en in-order traversering.

Mens vi er i gang, skal vi fjerne begrensninger på datatype selv. De faktisk gir ingen nytte, og har blitt fjernet fra språket som for Haskell 2011. (Når du ønsker å bruke slike begrensninger bør du sette dem på instanser av klasser, ikke på datatype.)

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

Først definerer vi hva det betyr for en liste for å være strengt sortert.

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.

Da kan vi bruke toListmetoden levert av Data.Foldable, og de ovennevnte hjelper.

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

Vi kan også gjennomføre dette mer direkte, som du spurte. Siden vi fjernet uønskede begrensninger på datatypen, kan vi forenkle definisjonen av en fold.

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)

Nå trenger vi en datatype for å modellere et resultat av vår catamorphism, som er at vi har heller ingen noder ( Z), eller en rekke strengt økende noder ( T) eller har sviktet ( X)

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

Og vi kan da gjennomføre isBSTdirekte

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

Dette er litt kjedelig, så kanskje det ville være bedre å dekomponere måten vi komponere den midlertidige tilstander litt:

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

Personlig ville jeg nok bare bruke Sammenleggbar eksempel.

Svarte 13/02/2011 kl. 15:31
kilden bruker

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