Heim > Web-Frontend > js-Tutorial > Konvertieren von Rekursion in Iteration mithilfe eines Stapels: Ein praktischer Leitfaden

Konvertieren von Rekursion in Iteration mithilfe eines Stapels: Ein praktischer Leitfaden

DDD
Freigeben: 2024-12-22 18:45:10
Original
888 Leute haben es durchsucht

Converting Recursion to Iteration Using a Stack: A Practical Guide

Rekursion ist eine leistungsstarke Technik in der Informatik, die häufig für Aufgaben wie Baumdurchquerung, Tiefensuche und Backtracking-Algorithmen verwendet wird. Aufgrund des Mehraufwands für Funktionsaufrufe und der Verwaltung des Aufrufstapels kann die Rekursion jedoch sowohl zeitlich als auch räumlich weniger effizient sein. In einigen Fällen ist es von Vorteil, die Rekursion in einen iterativen Ansatz umzuwandeln und einen expliziten Stapel zur Simulation der rekursiven Aufrufe zu verwenden. Dieser Artikel bietet eine Schritt-für-Schritt-Anleitung zum Konvertieren rekursiver Algorithmen in iterative mithilfe eines Stacks in JavaScript.


Warum Rekursion in Iteration umwandeln?

Es gibt mehrere Gründe, warum Sie Rekursion in Iteration umwandeln möchten:

  1. Stapelüberlauf: Tiefe rekursive Aufrufe können den Aufrufstapel erschöpfen und zu einem Stapelüberlauf führen. Durch die Verwendung eines expliziten Stacks kann dieses Problem vermieden werden.
  2. Effizienz: Iterative Lösungen sind im Allgemeinen speichereffizienter, da sie keinen Aufwand für die Wartung des Aufrufstapels erfordern.
  3. Bessere Kontrolle: Die Verwendung eines expliziten Stacks kann Ihnen mehr Kontrolle über die Ausführung des Algorithmus geben, insbesondere wenn es um Backtracking geht.

Vorlage zum Konvertieren von Rekursion in Iteration mithilfe eines Stapels

Beim Konvertieren einer rekursiven Funktion in eine iterative Funktion mithilfe eines Stapels bleibt der allgemeine Ansatz für verschiedene Arten von Algorithmen (wie Baumdurchläufe, Backtracking-Probleme oder Graphdurchläufe) ähnlich. Nachfolgend finden Sie eine flexible Vorlage, die an verschiedene Szenarien angepasst werden kann.


Allgemeine Vorlage

1. Rekursive Funktion (Beispiel)

function recursiveFunction(args) {
    // Base case
    if (baseCondition) {
        // Handle the base case
        return;
    }

    // Recursive calls
    for (let i = 0; i < someLimit; i++) {
        recursiveFunction(newArgs);
    }
}
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren

2. Iterative Funktion unter Verwendung eines Stapels

Um die obige rekursive Funktion in eine iterative umzuwandeln, führen wir die folgenden Schritte aus:

function iterativeFunction(args) {
    // Initialize the stack
    let stack = [initialState];

    // Loop until the stack is empty
    while (stack.length > 0) {
        // Pop the current state from the stack
        let currentState = stack.pop();

        // Handle the base case (optional, since we can check on each iteration)
        if (baseCondition) {
            continue;  // Skip or handle the base case
        }

        // Process the current state
        processState(currentState);

        // Push next states onto the stack
        for (let i = 0; i < someLimit; i++) {
            let newState = generateNewState(currentState, i);
            stack.push(newState);
        }
    }
}
Nach dem Login kopieren
Nach dem Login kopieren

Vorlagenaufschlüsselung

  1. Stack initialisieren:

    Der Stapel sollte mit dem Startzustand initialisiert werden, bei dem es sich um die Anfangsargumente oder den ersten Knoten in einer Durchquerung handeln kann.

  2. Schleife durch den Stapel:

    Die Schleife wird so lange fortgesetzt, wie der Stapel Elemente enthält, die die rekursiven Aufrufe darstellen, die in der ursprünglichen Funktion durchgeführt worden wären.

  3. Grundzustandsbehandlung:

    Bei der Rekursion prüft die Basisbedingung, ob eine weitere Rekursion erforderlich ist. Beim iterativen Ansatz können Sie die gleiche Prüfung innerhalb der Schleife durchführen. Mit „Weiter“ können Sie die weitere Verarbeitung überspringen, wenn die Grundbedingung erfüllt ist.

  4. Verarbeiten Sie den aktuellen Status:

    Verarbeiten Sie den Status der aktuellen Iteration (entspricht der Verarbeitung, die beim aktuellen rekursiven Aufruf erfolgen würde).

  5. Push Next States:

    Genau wie die rekursive Funktion neue rekursive Funktionen aufruft, schieben Sie hier die nächsten Zustände (d. h. zu verarbeitende Funktionsargumente oder Knoten) auf den Stapel.


Beispielkonvertierung: In-order Tree Traversal

Rekursive Version:

function recursiveFunction(args) {
    // Base case
    if (baseCondition) {
        // Handle the base case
        return;
    }

    // Recursive calls
    for (let i = 0; i < someLimit; i++) {
        recursiveFunction(newArgs);
    }
}
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren

Iterative Version mit einem Stack:

function iterativeFunction(args) {
    // Initialize the stack
    let stack = [initialState];

    // Loop until the stack is empty
    while (stack.length > 0) {
        // Pop the current state from the stack
        let currentState = stack.pop();

        // Handle the base case (optional, since we can check on each iteration)
        if (baseCondition) {
            continue;  // Skip or handle the base case
        }

        // Process the current state
        processState(currentState);

        // Push next states onto the stack
        for (let i = 0; i < someLimit; i++) {
            let newState = generateNewState(currentState, i);
            stack.push(newState);
        }
    }
}
Nach dem Login kopieren
Nach dem Login kopieren

Beispiele für die Konvertierung von Rekursion in Iteration

Beispiel 1: Tiefensuche (DFS) in einem Diagramm

Depth-First Search (DFS) wird üblicherweise mithilfe von Rekursion implementiert. Hier ist der rekursive DFS-Algorithmus:

function inorderTraversal(root) {
    if (root === null) return;
    inorderTraversal(root.left);
    console.log(root.value);
    inorderTraversal(root.right);
}
Nach dem Login kopieren
Nach dem Login kopieren

Iterative Version mit einem Stapel:

function inorderTraversalIterative(root) {
    let stack = [];
    let current = root;

    while (stack.length > 0 || current !== null) {
        // Reach the leftmost node
        while (current !== null) {
            stack.push(current);
            current = current.left;
        }

        // Visit the node
        current = stack.pop();
        console.log(current.value);

        // Move to the right node
        current = current.right;
    }
}
Nach dem Login kopieren
Nach dem Login kopieren

In diesem Beispiel enthält der Stapel explizit die Knoten, die besucht werden sollen, und wir verwenden die Schleife, um die rekursiven Aufrufe zu simulieren.


Beispiel 2: In-order Tree Traversal (iterativ)

Das Durchlaufen eines Binärbaums in der richtigen Reihenfolge erfolgt normalerweise rekursiv. Hier ist die rekursive Version:

function dfs(graph, node, visited = new Set()) {
    if (visited.has(node)) return;
    console.log(node);
    visited.add(node);

    for (let neighbor of graph[node]) {
        dfs(graph, neighbor, visited);
    }
}
Nach dem Login kopieren

Iterative Version mit einem Stapel:

function dfsIterative(graph, startNode) {
    let stack = [startNode];
    let visited = new Set();

    while (stack.length > 0) {
        let node = stack.pop();

        if (visited.has(node)) continue;

        console.log(node);
        visited.add(node);

        // Add neighbors to the stack in reverse order to maintain DFS order
        for (let neighbor of graph[node].reverse()) {
            if (!visited.has(neighbor)) {
                stack.push(neighbor);
            }
        }
    }
}
Nach dem Login kopieren

In diesem Fall hilft der Stapel dabei, zu besuchende Knoten zu verfolgen, und die innere Schleife durchläuft die linke Seite des Baums nach unten, bis sie den Knoten ganz links erreicht.


Beispiel 3: Teilmengen generieren (Backtracking)

Der Backtracking-Ansatz zum Generieren von Teilmengen einer Menge kann wie folgt rekursiv implementiert werden:

function inorderTraversal(root) {
    if (root === null) return;
    inorderTraversal(root.left);
    console.log(root.value);
    inorderTraversal(root.right);
}
Nach dem Login kopieren
Nach dem Login kopieren

Iterative Version mit einem Stapel:

function inorderTraversalIterative(root) {
    let stack = [];
    let current = root;

    while (stack.length > 0 || current !== null) {
        // Reach the leftmost node
        while (current !== null) {
            stack.push(current);
            current = current.left;
        }

        // Visit the node
        current = stack.pop();
        console.log(current.value);

        // Move to the right node
        current = current.right;
    }
}
Nach dem Login kopieren
Nach dem Login kopieren

Die iterative Version verwendet einen Stapel, um die rekursiven Funktionsaufrufe zu simulieren. Das currentSubset wird direkt geändert und der Stack übernimmt das Backtracking, indem er neue Zustände darauf schiebt.


Beispiel 4: Permutationen generieren

Um alle Permutationen einer Menge zu generieren, wird normalerweise Rekursion verwendet:

function subsets(nums) {
    let result = [];
    function backtrack(start, currentSubset) {
        result.push([...currentSubset]);
        for (let i = start; i < nums.length; i++) {
            currentSubset.push(nums[i]);
            backtrack(i + 1, currentSubset);
            currentSubset.pop();
        }
    }
    backtrack(0, []);
    return result;
}
Nach dem Login kopieren

Iterative Version mit einem Stapel:

function subsetsIterative(nums) {
    let stack = [{start: 0, currentSubset: []}];
    let result = [];

    while (stack.length > 0) {
        let { start, currentSubset } = stack.pop();
        result.push([...currentSubset]);

        // Explore subsets by including elements from `start` onwards
        for (let i = start; i < nums.length; i++) {
            currentSubset.push(nums[i]);
            stack.push({ start: i + 1, currentSubset: [...currentSubset] });
            currentSubset.pop(); // backtrack
        }
    }

    return result;
}
Nach dem Login kopieren

Diese iterative Version verwendet den Stapel, um den aktuellen Status der Permutation zu speichern. Das Backtracking wird durch das Verschieben und Entfernen der Zustände vom Stapel erledigt.


Beispiel 5: N-Queens-Problem (Backtracking)

Das N-Queens-Problem wird oft durch rekursives Backtracking gelöst:

function permute(nums) {
    let result = [];
    function backtrack(start) {
        if (start === nums.length) {
            result.push([...nums]);
            return;
        }
        for (let i = start; i < nums.length; i++) {
            [nums[start], nums[i]] = [nums[i], nums[start]];  // swap
            backtrack(start + 1);
            [nums[start], nums[i]] = [nums[i], nums[start]];  // backtrack (swap back)
        }
    }
    backtrack(0);
    return result;
}
Nach dem Login kopieren

Iterative Version mit einem Stapel:

function recursiveFunction(args) {
    // Base case
    if (baseCondition) {
        // Handle the base case
        return;
    }

    // Recursive calls
    for (let i = 0; i < someLimit; i++) {
        recursiveFunction(newArgs);
    }
}
Nach dem Login kopieren
Nach dem Login kopieren
Nach dem Login kopieren

Abschluss

Die Umwandlung von Rekursion in Iteration mithilfe eines Stapels ist eine wertvolle Technik für viele Algorithmen, insbesondere für solche, die Backtracking oder Baum-/Graph-Traversal beinhalten. Durch die Verwendung eines expliziten Stapels können wir tiefe Rekursionen vermeiden, Funktionszustände manuell verwalten und sicherstellen, dass wir eine bessere Kontrolle über die Ausführung des Algorithmus haben. Diese Beispiele sollen als Leitfaden dienen, um Ihnen bei der Bewältigung ähnlicher Probleme in Ihrem eigenen Code zu helfen.

Das obige ist der detaillierte Inhalt vonKonvertieren von Rekursion in Iteration mithilfe eines Stapels: Ein praktischer Leitfaden. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Quelle:dev.to
Erklärung dieser Website
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn
Beliebte Tutorials
Mehr>
Neueste Downloads
Mehr>
Web-Effekte
Quellcode der Website
Website-Materialien
Frontend-Vorlage