Intuitiv verstehe ich, dass ich auf Stack-Paare wie halten, aber immer noch kann ich nicht zu einer funktionierenden Lösung.Was ist der Algorithmus, um einen nicht-binären Baum ohne Rekursion (mit Stack) zu durchlaufen
Antwort
Sie können einen rekursiven Algorithmus immer in einen mit einem expliziten Stapel übersetzen. Beginnen Sie mit rekursiven Code:
void traverse(NODE *p) {
if (p) {
for (int i = 0; i < p->n_children; ++i) {
traverse(p->child[i]);
}
}
}
den rekursiven Aufruf ersetzen:
struct {
NODE *p;
int i;
} stk[MAX_RECURSION_DEPTH];
int sp = 0;
void traverse(NODE *p) {
start:
if (p) {
for (int i = 0; i < p->n_children; ++i) {
// Save local values on stack.
stk[sp].p = p;
stk[sp++].i = i;
// Simulate recursive call.
p = p->child[i];
goto start;
// Goto this label for return.
rtn:
}
}
// Simulate recursive return, restoring from stack if not empty.
if (sp == 0) return;
p = stk[--sp].p;
i = stk[sp].i;
goto rtn;
}
Da haben Sie es: eine explizite Stack-Implementierung, die so lange wie die rekursive Version tat arbeiten muss. Das ist gleich.
Jetzt, wenn Sie möchten, tun wir eine Algebra, um die goto
s zu beseitigen. Zunächst können wir die for
als while
umschreiben und Refactoring die rtn
Label
void traverse(NODE *p) {
int i;
start:
if (p) {
i = 0;
rtn_2:
while (i < p->n_children) {
stk[sp].p = p;
stk[sp++].i = i;
p = p->child[i];
goto start;
}
}
if (sp == 0) return;
p = stk[--sp].p;
i = stk[sp].i;
++i;
goto rtn_2;
}
Notiere die ++i
innerhalb des while
war tot Code, so sicher war zu fallen.
Jetzt beachten Sie, dass der Körper der while
nie mehr als einmal ausgeführt wird. Es kann durch eine if
ersetzt werden. Wir können goto rtn_2
auch durch den Code ersetzen, der ausgeführt wird.
void traverse(NODE *p) {
int i;
start:
if (p) {
i = 0;
if (i < p->n_children) {
stk[sp].p = p;
stk[sp++].i = i;
p = p->child[i];
goto start;
}
}
for (;;) {
if (sp == 0) return;
p = stk[--sp].p;
i = stk[sp].i;
++i;
if (i < p->n_children) {
stk[sp].p = p;
stk[sp++].i = i;
p = p->child[i];
goto start;
}
}
}
Schließlich können wir von dem start
Etikett loszuwerden, indem eine Schleife statt mit:
void traverse(NODE *p) {
int i;
for (;;) {
if (p) {
i = 0;
if (i < p->n_children) {
stk[sp].p = p;
stk[sp++].i = i;
p = p->child[i];
continue;
}
}
for (;;) {
if (sp == 0) return;
p = stk[--sp].p;
i = stk[sp].i;
++i;
if (i < p->n_children) {
stk[sp].p = p;
stk[sp++].i = i;
p = p->child[i];
break;
}
}
}
}
Eine weitere Bereinigung zu beachten ist, dass i
ist immer 0 in den ersten if
und die continue
ist eigentlich Implementieren einer geschachtelten Schleife, die wir explizit machen können. Es gibt auch eine redundante stk[sp].p = p;
, die gelöscht werden kann. Es ist das Kopieren nur Wert auf den Stapel, der bereits vorhanden ist:
void traverse(NODE *p) {
for (;;) {
while (p && p->n_children > 0) {
stk[sp].p = p;
stk[sp++].i = 0;
p = p->child[0];
}
for (;;) {
if (sp == 0) return;
p = stk[--sp].p;
int i = stk[sp].i + 1;
if (i < p->n_children) {
stk[sp++].i = i; // stk[sp].p = p; was redundant, so deleted
p = p->child[i];
break;
}
}
}
}
Es ist möglich, den Code ein bisschen bisschen besser zu machen, aber ich werde es dich verlassen. Zu beachten ist, dass es keine Intuition gab oder sich vorzustellen versuchte, was Zeiger tun. Wir haben gerade den Code in Algebra erklärt, und es ergab sich eine einigermaßen nette Implementierung. Ich habe es nicht ausgeführt, aber wenn ich keinen Algebra-Fehler gemacht habe (was möglich ist), sollte das "einfach funktionieren".
Beachten Sie, dass dies ein wenig anders ist als das typische Stack-basierte DFS, das Sie in Lehrbüchern sehen. Diese schieben alle Kinder eines neu gefundenen Knotens auf den Stapel, was als erstes Kind gemacht werden muss, um eine normale DFS-Reihenfolge zu erhalten.
Stattdessen drücken wir den Elternteil zusammen mit einer Ganzzahl, die sagt, welches Kind als nächstes gesucht werden soll. Dies ist der von Ihnen erwähnte Knoten + Iterator. Es ist ein bisschen komplexer, aber auch effizienter in der Stapelgröße. Die maximale Größe unseres Stapels ist O (D), wobei D die maximale Tiefe des Baumes ist. Die Größe des Stapels in dem Lehrbuchalgorithmus ist O (KD), wobei K die maximale Anzahl von Kindern ist, die ein Knoten haben kann.
- 1. Wie baue ich einen Baum ohne Rekursion aus einem Baum?
- 2. Stack-Overflow-Ausnahme ohne Rekursion
- 3. Was ist der effiziente Algorithmus zum Implementieren von Stack?
- 4. Rekursion in Baum Traversal
- 5. Variable Anzahl von for-Schleifen ohne Rekursion aber mit Stack?
- 6. CTE Rekursion geordneter Baum
- 7. Nicht-binäre Baum Rekursion
- 8. Wie AST-Baum durchlaufen
- 9. Javascript Rekursion Baum Gebäude
- 10. Was ist der richtige Weg, um einen Annulus (Wide Circle) mit Bresenham-Algorithmus zu zeichnen?
- 11. Was ist der richtige Weg, um einen Baumknoten zu löschen
- 12. Was ist der richtige Weg, um mit einem Fehler zu sterben, aber ohne einen Stack-Trace in Perl?
- 13. Ein Array-Stack-Algorithmus ohne Kopie
- 14. Was ist der beste Weg, um Ajax Anrufe durchlaufen
- 15. Was ist der beste Weg, um einen Baum aus einem Git-Repository zu extrahieren?
- 16. Was ist der sichere Weg, um einen Vektor zu durchlaufen, und löschen Sie seine bestimmten Elemente
- 17. C# Stack-Überlauffehler mit Rekursion
- 18. Vorteil der Neuimplementierung der Rekursion mit manuell verwaltetem Stack?
- 19. Was ist der idiomatische Weg, um einen Stack für `Dictionary`-Typ in` Python` zu implementieren?
- 20. Implementieren Sie einen Algorithmus, um einen Knoten in eine kreisförmige verkettete Liste einzufügen, ohne ihn zu durchlaufen
- 21. Javascript Rekursion (Baum- Structure)
- 22. Java-Rekursion und binärer Baum
- 23. C# Baum/Sammlung Algorithmus
- 24. Algorithmus Rekursion Sortierung
- 25. Wie kann ich den HTML-Baum mit Jsoup durchlaufen?
- 26. Was ist der beste Weg, um einen geschwindigkeitsbegrenzenden Algorithmus für Web-Anfragen zu implementieren?
- 27. Java Rekursion Iterator mit meinem eigenen Baum
- 28. Stack-Überlauffehler in Java-Rekursion
- 29. Was ist der richtige Weg, um eine Liste mit einem Element in Python zu durchlaufen?
- 30. Was ist der Unterschied zwischen Rekursion und Klasseninstanz Rekursion
Welche Sprache verwenden Sie, können Sie den Code veröffentlichen, an dem Sie gearbeitet haben? – zenwraight
Jedes Mal, wenn Sie den Stapel auffüllen, erhöhen Sie den untergeordneten Iterator. Wenn es ein nächstes Kind gibt, drücke es auf den Stapel und wiederhole es. Wenn nicht, blenden Sie den aktuellen Knoten ein. Dies folgt direkt aus dem Sonderfall für binäre Bäume. – meowgoesthedog