2009-10-23 16 views
8

Was ist der beste Weg, alle Knoten einer verknüpften Struktur zu besuchen (alle Knoten haben Verweise auf Eltern und alle Kinder, Wurzelknoten haben Null als Eltern), so dass kein Knoten vor irgendwelchen besucht wird von seinen Vorfahren? Brownie Punkte für nicht-rekursive.Gehen einen Baum, Eltern zuerst

+1

Verbunden: http://stackoverflow.com/questions/754439/iterative-tree-walking –

Antwort

6

Pseudo-Code:

NodesToVisit = some stack or some list 
NodesToVisit.Push(RootNode) 

While NodesToVisit.Length > 0 
{ 
    CurNode = NodesToVisit.Pop() 
    For each Child C in CurNode 
     NodesToVisit.Push(C) 
    Visit(CurNode) (i.e. do whatever needs to be done) 
} 

bearbeiten: rekursive oder nicht?
Um technisch korrekt und wie durch AndreyT und andere in diesem Beitrag erwähnt, ist dieser Ansatz eine Form von ein rekursiver Algorithmus, wobei ein explizit verwaltet Stapel anstelle der CPU-Stack verwendet wird und wo die Rekursion findet auf der Ebene der While-Schleife statt. Das heißt, es ist von einer rekursiven Implementierung unterscheidet per se in ein paar feine, aber signifikante Möglichkeiten:

  • Nur die „Variablen“ werden auf den Stapel geschoben; Es gibt keinen "Stapelrahmen" und eine zugeordnete Rücksprungadresse auf dem Stapel, die einzige "Rücksprungadresse" ist implizit in der while-Schleife, und es gibt nur eine Instanz davon.
  • Der "Stapel" könnte als eine Liste verwendet werden, wobei der nächste "Rahmen" irgendwo in der Liste genommen werden könnte, ohne die Logik in irgendeiner Weise zu bremsen.
+0

OK. Dies war keine akademische Frage. Es war eine praktische Frage. Dies beantwortet es in zufriedenstellender Weise, ohne mich zum Nachdenken zu bringen oder weiter zu lesen. Vielen Dank. (Es wird mich wahrscheinlich später denken lassen, wenn ich die Zeit bekomme, aber das ist in Ordnung ... Nützlich, sogar ...) Job ist erledigt. Danke nochmal :) – George

0

Erstellen Sie eine Liste der Knoten im Stammverzeichnis (Ebene 0), durchlaufen Sie nacheinander alle Knoten und suchen Sie nach direkten untergeordneten Elementen (deren übergeordneter Knoten der aktuelle Knoten ist) (Ebene 1) level 0 geht weiter zu iterating level 1 und so weiter, bis Sie keine noch nicht besuchten Knoten mehr haben.

1

Verwenden Sie eine Reihe von Knoten. Setzen Sie die Wurzel in das Set, um zu starten. Ziehen Sie dann in einer Schleife einen Knoten aus der Menge, besuchen Sie ihn und legen Sie dann seine untergeordneten Elemente in die Menge ein. Wenn das Set leer ist, sind Sie fertig.

+0

Sie möchten, dass die Datenstruktur FIFO ist, nicht irgendein alter Container, um die Vorbestellungsbedingung zu garantieren. –

+0

Es gab keine solche Anforderung in der Frage. –

1

In Pseudo-Code:

currentList = list(root) 
nextList = list() 
while currentList.count > 0: 
    foreach node in currentList: 
     nextList.add(node.children) 
    currentList = nextList 
3

Sie suchen ein Vorordnungsdurchquerung suchen. Ich denke, Sie können es nicht rekursiv mit eine Warteschlange tun:. In Pseudocode:

Erstellen Sie eine leere Warteschlange, dann drücken Sie den Stammknoten.

while nonempty(q) 
    node = pop(q) 
    visit(node) 
    foreach child(node) 
    push(q,child) 
+0

Das wäre eine * nicht rekursive Implementation * eines * rekursiven Algorithmus *. Durch Ersetzen des impliziten Stapels durch den expliziten Stapel wird kein rekursiver Algorithmus in einen nicht-rekursiven Algorithmus umgewandelt. Tatsächlich ändert es den Algorithmus überhaupt nicht. Was Sie oben haben, ist offensichtlich rekursiv (soweit es den Algorithmus betrifft). – AnT

+5

Wenn Leute sagen, dass sie keine Rekursion wollen, beziehen sie sich typischerweise auf die Rekursion auf Funktionsebene. Dies erfüllt diese Bedingung. –

+0

Nun, manchmal ja. Das Problem, das wir hier betrachten, ist jedoch spezifisch gestaltet, um eine wirklich nicht-rekursive Lösung (d. H. Nicht-rekursiven Algorithmus) zu ermöglichen. Das totale Werbegeschenk ist die Anwesenheit von Elternzeigern. Ihre "nicht-rekursive" Lösung verwendet keine übergeordneten Zeiger. Wundern Sie sich nicht, warum sie überhaupt da sind? Sie sind dort speziell, um eine echte nicht-rekursive Lösung zu ermöglichen, die mit O (1) Speicherbedarf. – AnT

1

Wenn Sie an dem Wurzelknoten zu starten, und besuchen nur die Eltern/Kinder von Knoten, die Sie bereits besucht haben, gibt es keine Möglichkeit, den Baum so zu durchqueren, dass Sie einen Knoten besuchen, bevor seine Vorfahren zu besuchen.

Jede Art von Traversierung, Tiefe zuerst (rekursiv/stackbasiert), Breite zuerst (queuebasiert), tiefenlimitiert oder einfach aus einer ungeordneten Menge herausziehen funktioniert.

Die "beste" Methode hängt vom Baum ab. Breite zuerst würde gut für einen sehr hohen Baum mit wenigen Zweigen funktionieren. Tiefe zuerst würde gut für Bäume mit vielen Zweigen funktionieren.

Da die Knoten tatsächlich Zeiger auf ihre Eltern haben, gibt es auch einen Konstantspeicher-Algorithmus, aber es ist viel langsamer.

+0

Der Op sagte "* nein * Knoten wird vor seinen Vorfahren besucht". Es ist also umgekehrt. – AnT

+0

Was ist umgekehrt? Hast du meine Antwort richtig gelesen? – Artelius

+0

Vielleicht nicht. Ich dachte, dass Sie in Ihrem ersten Satz behauptet haben, dass das Problem nicht gelöst werden kann, da der Besuchsauftrag (den ich angenommen habe, dass Sie falsch verstanden haben) unmöglich zu befriedigen ist. – AnT

3

Wenn Sie Links zu allen untergeordneten Elementen und auch zum übergeordneten Element haben, ist der nicht-rekursive Algorithmus eher trivial. Vergiss einfach, dass du einen Baum hast. Stellen Sie sich vor, es ist ein Labor, in dem jede Eltern-Kind-Verbindung ein normaler bidirektionaler Korridor von einer Kreuzung zur anderen ist. Alles, was Sie tun müssen, um das gesamte Labyrinth zu durchqueren, besteht darin, bei jeder Kreuzung in den nächsten Gang links abzubiegen. (Alternativ können Sie sich vorstellen, dass Sie mit Ihrer linken Hand durch das Labyrinth gehen und immer die Wand auf der linken Seite berühren). Wenn Sie an der Wurzelkreuzung beginnen (und sich in eine beliebige Richtung bewegen), werden Sie den ganzen Baum durchwandern und immer die Eltern vor den Kindern besuchen. Jeder "Korridor" wird in diesem Fall zweimal (in die eine Richtung und in die andere) gereist, und jede "Kreuzung" (Knoten) wird so oft besucht, wie viele "Korridore" sich ihm anschließen.

+0

Dies ist der konstante Speicher-Algorithmus, den ich erwähnt habe. – Artelius

1

Ich würde mit der ersten Suche der Breite nicht übereinstimmen, da Raumkomplexität häufig der Fluch dieses spezifischen Suchalgorithmus ist. Möglicherweise ist die Verwendung des iterativen Vertiefungsalgorithmus für diese Art der Verwendung eine bessere Alternative und deckt den gleichen Traversierungstyp wie die erste Breite der Suche ab. Es gibt kleine Unterschiede im Umgang mit dem Rand von der Breite-zuerst-Suche, es sollte nicht zu hart sein, um (Pseudo-) code-out, obwohl.

Referenz: http://en.wikipedia.org/wiki/Iterative_deepening

+0

+1 wegen Ihrer Überlegung Raumkomplexität - aber warum nicht nur eine Tiefensuche zuerst verwenden? – Artelius

+0

Viele Bäume in der Praxis neigen dazu, tiefer als sie sind "breiter", vor allem. in KI Entscheidungsprozessen. Die Frage besagt nicht, ob der Baum endlich ist, aber Sie könnten in eine Schleife geraten. Einer der Gründe, warum iterative Vertiefung gemocht wird, ist, dass es vollständig ist (wird eine Lösung finden). – mduvall

1

Hier ist ein wirklich nicht-rekursive Ansatz: kein Stapel, konstanter Raum. Dieser Python-Code wird davon ausgegangen, dass jeder Knoten eine Liste von Kindern enthält, und dass die Knotenobjekte definieren nicht Gleichheit, so dass die ‚Index‘ Funktion Identitäten vergleicht:

def walkTree(root, visit_func): 
    cur = root 
    nextChildIndex = 0 

    while True: 
     visit_func(cur) 

     while nextChildIndex >= len(cur.children) and cur is not root: 
      nextChildIndex = cur.parent.children.index(cur) + 1 
      cur = cur.parent 

     if nextChildIndex >= len(cur.children): 
      break 

     cur = cur.children[nextChildIndex] 
     nextChildIndex = 0 

Ich bin sicher, dass es aufpoliert werden könnte ein bisschen, übersichtlicher und einfacher zu lesen, aber das ist das Wesentliche.