Finden Sie in Haskell heraus, ob ein Baum ein binärer Suchbaum ist

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)

Was ich tun möchte, ist, eine Funktion zu schreiben, um zu bestimmen, ob der angegebene Baum ein binärer Suchbaum ist. Meine Methode besteht darin, alle Werte in einer Liste zu gruppieren und zu importieren Data.Listund dann die Liste zu sortieren, um festzustellen, ob sie gleich sind, aber es ist etwas kompliziert. Können wir dies tun, ohne ein anderes Modul zu importieren?

Jayyyyyy
quelle
Ich würde nicht flattenTreezuerst definieren . Sie können Falsefrühzeitig zurückkehren, wenn ein Knoten die Sucheigenschaft verletzt, ohne den gesamten auf diesem Knoten verwurzelten Teilbaum durchlaufen zu müssen.
Chepper
@chepner das problem ist mit sort, nicht mit flattenTree, was faul genug ist.
Will Ness
Ja, das kam mir in den Sinn, nachdem ich mir einige der anderen Antworten angesehen hatte.
Chepper

Antworten:

13

Hier ist eine Möglichkeit, dies zu tun, ohne den Baum zu plattieren.

Aus der Definition hier

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

Man kann sehen, dass das Durchlaufen des Baums von links nach rechts, Ignorieren Nodeund Klammern eine abwechselnde Folge von Nulls und as ergibt . Das heißt, zwischen jeweils zwei Werten befindet sich ein Null.

Mein Plan ist es, zu überprüfen, ob jeder Teilbaum die entsprechenden Anforderungen erfüllt : Wir können die Anforderungen an jedem Teil verfeinernNode , uns daran erinnern, zwischen welchen Werten wir liegen, und sie dann an jedem testenNull . Da es Nullzwischen jedem Wertepaar in der Reihenfolge ein Wertpaar gibt, haben wir getestet, dass alle in der Reihenfolge (von links nach rechts) liegenden Paare nicht abnehmen.

Was ist eine Anforderung? Es ist eine lose Unter- und Obergrenze für die Werte im Baum. Um Anforderungen auszudrücken, einschließlich der Anforderungen am linken und rechten Ende, können wir jede Bestellung mit BotTom und TopElementen wie folgt erweitern:

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

Lassen Sie uns nun überprüfen, ob ein bestimmter Baum die Anforderungen erfüllt, sowohl in der richtigen Reihenfolge als auch zwischen den angegebenen Grenzen zu sein.

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

Ein binärer Suchbaum ist ein Baum, der in der Reihenfolge und zwischen Botund angeordnet ist Top.

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

Wenn Sie die tatsächlichen Extremwerte in jedem Teilbaum berechnen und nach außen sprudeln, erhalten Sie mehr Informationen als Sie benötigen, und dies ist in den Randfällen, in denen ein linker oder rechter Teilbaum leer ist, schwierig. Das Aufrechterhalten und Überprüfen der Anforderungen , das Verschieben nach innen, ist eher einheitlicher.

Schweinearbeiter
quelle
6

Hier ist ein Hinweis: Machen Sie eine Hilfsfunktion

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

wo BSTResult aist definiert als

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

Sie sollten in der Lage sein, rekursiv vorzugehen und die Ergebnisse von Teilbäumen zu nutzen, um die Berechnung voranzutreiben, insbesondere das Minimum und das Maximum.

Zum Beispiel, wenn Sie tree = Node left 20 rightmit isBSTree' left = NonEmptyBST 1 14und haben isBSTree' right = NonEmptyBST 21 45, dann isBSTree' treesollte sein NonEmptyBST 1 45.

Im gleichen Fall außer tree = Node left 24 rightsollten wir stattdessen haben isBSTree' tree = NotBST.

Das Konvertieren des Ergebnisses in Boolist dann trivial.

Chi
quelle
1
oder definieren Sie das offensichtliche Monoid für BSTResult aund falten Sie es ein. :) (oder auch wenn es kein rechtmäßiger Monoid ist ....)
Will Ness
(aber es ist trotzdem legal, denke ich)
Will Ness
3

Ja , Sie müssen die Liste nicht sortieren. Sie können überprüfen, ob jedes Element kleiner oder gleich dem nächsten Element ist. Das ist effizienter , da wir dies tun können , in O (n) , während die sortierte Liste Auswertung vollständig nimmt O (n log n) .

Wir können dies also überprüfen mit:

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

So können wir überprüfen, ob der Binärbaum ein binärer Suchbaum ist mit:

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

Ich denke, man kann behaupten, dass es Nullsich um einen binären Suchbaum handelt, da es sich um einen leeren Baum handelt. Dies bedeutet also, dass für jeden Knoten (es gibt keine Knoten) die Elemente im linken Teilbaum kleiner oder gleich dem Wert im Knoten sind und die Elemente im rechten Teilbaum alle größer oder gleich dem Wert im Knoten sind .

Willem Van Onsem
quelle
1

Wir können von links nach rechts über den Baum gehen:

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

Inspiriert von John McCarthygopher .

Die explizite Pushdown-Liste kann durch Weitergabe entfernt werden.

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)

Es reicht aus, nur ein bislang größtes Element beizubehalten.

Will Ness
quelle