Algorithmus zum Testen, ob ein Binärbaum ein Suchbaum ist, und zum Zählen vollständiger Zweige

10

Ich muss einen rekursiven Algorithmus erstellen, um zu sehen, ob ein Binärbaum ein binärer Suchbaum ist, und um zu zählen, wie viele vollständige Zweige vorhanden sind (ein übergeordneter Knoten mit sowohl linken als auch rechten untergeordneten Knoten) mit einer angenommenen globalen Zählvariablen. Dies ist eine Zuordnung für meine Datenstrukturklasse.

Soweit habe ich

void BST(tree T) {
   if (T == null) return
   if ( T.left and T.right) {
      if (T.left.data < T.data or T.right.data > T.data) {
        count = count + 1
        BST(T.left)
        BST(T.right)
      }
   }
}

Aber ich kann das nicht wirklich herausfinden. Ich weiß, dass dieser Algorithmus das Problem nicht lösen wird, da die Anzahl Null ist, wenn die zweite if-Anweisung nicht wahr ist.

Könnte mir jemand dabei helfen?

OghmaOsiris
quelle
Wie ist der Vergleichsoperator <auf Knoten definiert?
Joe
Möchten Sie die Anzahl berechnen, auch wenn es sich nicht um einen binären Suchbaum handelt?
Joe
1
Soll Ihr Algorithmus etwas zurückgeben, wie trueoder false?
Joe
2
Vielleicht sollten Sie zuerst versuchen, zwei separate Funktionen zu definieren: eine zum Überprüfen, ob es sich um eine BST handelt, und eine zum Zählen der vollständigen Zweige. Das sollte überschaubarer sein.
7.
1
@OghmaOsiris Ich kann mir vorstellen, dass er das gesagt hat, weil die Frage im Grunde "Hier ist mein Code, wie mache ich das?" Lautet. Wenn der Code nicht pseudo (ish) wäre, wäre es definitiv eine SO-Frage.
sepp2k

Antworten:

10

Wie andere bereits in Kommentaren angegeben haben, haben Sie hier wirklich zwei nicht miteinander verbundene Funktionen: Testen, ob der Baum ein Suchbaum ist, und Zählen der vollständigen Zweige. Wenn die Aufgabe dies nicht ausdrücklich erfordert, würde ich zwei separate Funktionen schreiben.

Lassen Sie uns zuerst sehen, wie die gesamten Zweige gezählt werden. Das bedeutet, die Knoten zu zählen, die sowohl ein linkes als auch ein rechtes Kind haben. Dann müssen Sie den Zähler ( count = count + 1) erhöhen, wenn beide T.leftund T.rightnicht null sind (nicht T.left.dataund T.right.data: Die Daten spielen für diese Aufgabe keine Rolle).

if (T.left and T.right) {
    count = count + 1

Darüber hinaus müssen Sie den linken Teilbaum untersuchen, auch wenn der rechte Teilbaum leer ist, und Sie müssen den rechten Teilbaum untersuchen, auch wenn der linke Teilbaum leer ist. Beobachten Sie also, wo Sie die rekursiven Aufrufe platzieren.

Um zu testen, ob der Baum ein Suchbaum ist, müssen Sie die Datenwerte überprüfen. Sie haben bereits etwas in der Nähe des richtigen Vergleichs. nicht ganz richtig. Schreiben Sie einige Beispielbäume mit verschiedenen Formen (nicht sehr groß, 2 bis 5 Knoten) und führen Sie Ihren Algorithmus darauf aus, um zu sehen, was passiert.

Sie müssen noch einen Platz finden, an dem Sie das Ergebnis der Gültigkeitsprüfung ablegen können. Beobachten Sie erneut, wo Sie die rekursiven Aufrufe platzieren (wenn Sie nur diesen Teil ausführen, gibt es mehrere Lösungen, aber machen Sie sich in diesem Stadium keine Sorgen, wenn Sie nur eine sehen).

Wenn Sie es schließlich geschafft haben, beide Funktionen separat zu schreiben und sie an einigen Beispielen getestet haben, setzen Sie sie sorgfältig zusammen (falls dies für die Zuweisung erforderlich ist).

Gilles 'SO - hör auf böse zu sein'
quelle
danke, ich habe die Frage noch einmal gelesen und es sollte separate Methoden sein.
OghmaOsiris
7

In solchen Dingen ist es oft einfacher, rückwärts zu denken. Überlegen Sie also zuerst, was Sie brauchen. Lassen Sie uns anhand Ihrer Beschreibung Folgendes auflisten:

  • Rekursion
  • Gültigkeit
  • Anzahl der vollständigen Knoten

OK, das ist eine ziemlich kurze Liste, diese sollte überschaubar sein. Beginnen wir mit einer leeren Methode und fügen eine Beschreibung hinzu, was passieren soll.

valid_bst () {
}

Jetzt Gültigkeit. Wie prüft man die Gültigkeit? Im Chat sagten Sie, ein Baum sei gültig, "wenn ... alle linken Kinder kleiner als die Eltern sind und die rechten Kinder größer als die Eltern". Ich bin sicher, Sie wollten auch Gleichheit zulassen. Das wäre t.left.value <= t.value <= t.right.value.

valid_bst () {
    This node is valid if t.left.value <= t.value <= t.right.value
}

Aber was ist, wenn eines der Kinder vermisst wird? Nach allem, was Sie gesagt haben, wissen Sie, dass der Knoten noch gültig ist, wenn einer fehlt (oder beide fehlen). Fügen wir dies hinzu und strukturieren uns leicht um:

valid_bst () {
    This node is valid to the left if 
        there is no left child or 
        it is no greater than the current node.
    This node is valid to the right if 
        there is no right child or 
        it is no less than the current node.
    This node is valid overall if it is valid to the left and right.
}

OK, wir wissen jetzt, ob dieser Knoten gültig ist. Wie prüfen wir, ob der gesamte Baum gültig ist? Es befindet sich nicht in einem Array, daher können / wollen wir es wahrscheinlich nicht linear durchlaufen. Ihre Aufgabe gibt die Antwort: Rekursion. Aber wie akkumulieren wir eine Antwort durch Rekursion? Wir haben Zugriff auf drei Informationen, ob dieser Knoten gültig ist, und das Ergebnis von Aufrufen, in denen gefragt wird, ob der linke und der rechte Knoten gültig sind. Offensichtlich ist der Baum nur gültig, wenn alle drei wahr sind.

valid_bst () {
    This node is valid to the left if 
        there is no left child or 
        it is no greater than the current node.
    This node is valid to the right if 
        there is no right child or 
        it is no less than the current node.
    This node is valid overall if it is valid to the left and right.
    Is the left child valid?
    Is the right child valid?
    This tree is only valid if this node and both its children are.
}

Wenn Sie aufpassen, sagt uns das sogar, was unsere Funktion zurückgeben muss.

Wie integrieren wir nun die Zählung? Sie sagen, was zählt ("ein übergeordneter Knoten mit linken und rechten untergeordneten Knoten"), und das sollte nicht schwer in tatsächlichen Code zu übersetzen sein. Überprüfen Sie, ob diese Bedingung erfüllt ist, und erhöhen Sie den Zähler entsprechend. Denken Sie daran, dass dies irgendwo sein muss, wo es jedes Mal erreicht wird, wenn es wahr ist.

Und natürlich habe ich einige Details wie die Rekursionsstoppbedingung weggelassen und nach Null gesucht.

Kevin
quelle
6

Meine drei obigen Kommentare sind drei Hinweise auf Probleme mit Ihrem Code.

  1. Wenn Sie nicht bereits speziell definiert haben, wie ein Vergleichsoperator mit dem Knotendatentyp umgehen soll, wird der direkte Vergleich zweier Knoten höchstwahrscheinlich nicht das tun, was Sie wollen. Was Sie wahrscheinlich gemeint haben, war zum Beispiel, die in den Knoten gespeicherten Felder zu vergleichennode1.value < node2.value
  2. Im Moment erhöhen Sie die Anzahl nur, wenn der dritte ifWert zutrifft. Sind Sie sicher, dass Sie dies beabsichtigt haben? Übrigens möchten Sie vielleicht überprüfen, ob die if-Anweisung das tut, was Sie wollen.
  3. Ich gehe davon aus, dass Sie true zurückgeben möchten, wenn der Baum eine gültige BST ist, andernfalls false. Dies bedeutet, dass Sie in einem Basisfall immer true oder false zurückgeben müssen und auch die Ergebnisse Ihrer rekursiven Aufrufe zurückgeben sollten.
Joe
quelle
Zu Punkt eins: Das ist Pseudocode, oder? Solange die Absicht dem Leser vermittelt wird, gibt es keinen Grund, solche Dinge zu definieren.
sepp2k
@ sepp2k das stimmt, und mein Kommentar ist wahrscheinlich ein bisschen zu wählerisch für Pseudocode. Ich denke, mein Punkt ist, dass wir verstehen müssen, was es bedeutet, zwei Knoten zu vergleichen. Ihr Punkt ist, dass wir das bereits implizit verstehen sollten.
Joe
Genau.
sepp2k