Arian Soleimanzadeh
  • Startseite
  • Blog
  • Podcasts
  • Videos
  • Kontakt
العربيةArabic
DeutschGerman
EnglishEnglish
فارسیPersian
한국어Korean
中文Chinese
Bereich•Schnellkontakt

Languages

Choose your interface locale

ar

العربية

Arabic

de

Deutsch

German

en

English

English

fa

فارسی

Persian

ko

한국어

Korean

zh

中文

Chinese

Termin vereinbaren

Senden Sie eine kurze Nachricht — ich antworte so bald wie möglich.

LinkedInSchnelle Antwort
Startseite/Artikel/Kosaraju mit JavaScript und TypeScript implementieren
KosarajuArtikel

Kosaraju mit JavaScript und TypeScript implementieren

Eine praktische Implementierung des Kosaraju-Algorithmus mit JavaScript und TypeScript: Graphdarstellung, DFS-Finish-Order, Transpose Graph, SCC-Ermittlung, Laufzeit und typische Fehler.

20. August 20269 Min. Lesezeit1 Aufrufe
#Kosaraju#JavaScript#TypeScript#Graph Algorithms#SCC#DFS#Software Engineering

Arian Soleimanzadeh

Software Engineer & Researcher

Kosaraju-Implementierung mit JavaScript und TypeScript, DFS, Transpose Graph und Strongly Connected Components

Arian Soleimanzadeh

KI · Code · Produkt

Forschung + Engineering
Auf dieser Seite
EinführungGraph mit einer Adjazenzliste darstellenErster DFS-DurchlaufTranspose Graph erstellenVollständige JavaScript-VersionTypeScript mit GenericsBeispiel: Microservice-AbhängigkeitenKomplexitätRekursion in JavaScriptTypische FehlerFazit

Einführung

Der Kosaraju-Algorithmus bestimmt Strongly Connected Components (SCCs) in gerichteten Graphen.

In diesem Artikel implementieren wir ihn mit JavaScript und TypeScript und betrachten dabei nicht nur den Code, sondern auch wichtige Implementierungsdetails.

Der Ablauf besteht aus:

Code
123456789
DFS auf Originalgraph
        ↓
Finish Order
        ↓
Kanten umkehren
        ↓
DFS auf Transpose Graph
        ↓
SCCs

Graph mit einer Adjazenzliste darstellen

JavaScript
1234567
const graph = new Map([
  [0, [1]],
  [1, [2, 3]],
  [2, [0]],
  [3, [4]],
  [4, [3]]
]);

Adjazenzlisten sind besonders für dünn besetzte Graphen geeignet und benötigen typischerweise O(V + E) Speicher.


Erster DFS-Durchlauf

Der Knoten wird erst nach der vollständigen Verarbeitung seiner Nachbarn auf den Stack gelegt:

JavaScript
1234567891011
function fillOrder(node, graph, visited, stack) {
  visited.add(node);

  for (const neighbor of graph.get(node) ?? []) {
    if (!visited.has(neighbor)) {
      fillOrder(neighbor, graph, visited, stack);
    }
  }

  stack.push(node);
}

Damit speichern wir die Finish Order und nicht lediglich die Reihenfolge der ersten Besuche.


Transpose Graph erstellen

Jede gerichtete Kante wird umgedreht:

Code
1
A → B

wird zu:

Code
1
B → A
JavaScript
12345678910111213141516171819
function transposeGraph(graph) {
  const transpose = new Map();

  for (const node of graph.keys()) {
    transpose.set(node, []);
  }

  for (const [node, neighbors] of graph.entries()) {
    for (const neighbor of neighbors) {
      if (!transpose.has(neighbor)) {
        transpose.set(neighbor, []);
      }

      transpose.get(neighbor).push(node);
    }
  }

  return transpose;
}

Vollständige JavaScript-Version

JavaScript
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364
function kosaraju(graph) {
  const visited = new Set();
  const stack = [];

  function fillOrder(node) {
    visited.add(node);

    for (const neighbor of graph.get(node) ?? []) {
      if (!visited.has(neighbor)) {
        fillOrder(neighbor);
      }
    }

    stack.push(node);
  }

  for (const node of graph.keys()) {
    if (!visited.has(node)) {
      fillOrder(node);
    }
  }

  const transpose = new Map();

  for (const node of graph.keys()) {
    transpose.set(node, []);
  }

  for (const [node, neighbors] of graph.entries()) {
    for (const neighbor of neighbors) {
      if (!transpose.has(neighbor)) {
        transpose.set(neighbor, []);
      }

      transpose.get(neighbor).push(node);
    }
  }

  visited.clear();
  const components = [];

  function collect(node, component) {
    visited.add(node);
    component.push(node);

    for (const neighbor of transpose.get(node) ?? []) {
      if (!visited.has(neighbor)) {
        collect(neighbor, component);
      }
    }
  }

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

    if (!visited.has(node)) {
      const component = [];
      collect(node, component);
      components.push(component);
    }
  }

  return components;
}

TypeScript mit Generics

TypeScript
1
type Graph<T> = Map<T, T[]>;

Dadurch können beispielsweise Service-Namen direkt als Vertex verwendet werden.

TypeScript
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364
function kosaraju<T>(graph: Graph<T>): T[][] {
  const visited = new Set<T>();
  const stack: T[] = [];

  const fillOrder = (node: T): void => {
    visited.add(node);

    for (const neighbor of graph.get(node) ?? []) {
      if (!visited.has(neighbor)) {
        fillOrder(neighbor);
      }
    }

    stack.push(node);
  };

  for (const node of graph.keys()) {
    if (!visited.has(node)) {
      fillOrder(node);
    }
  }

  const transpose: Graph<T> = new Map();

  for (const node of graph.keys()) {
    transpose.set(node, []);
  }

  for (const [node, neighbors] of graph.entries()) {
    for (const neighbor of neighbors) {
      if (!transpose.has(neighbor)) {
        transpose.set(neighbor, []);
      }

      transpose.get(neighbor)!.push(node);
    }
  }

  visited.clear();
  const components: T[][] = [];

  const collect = (node: T, component: T[]): void => {
    visited.add(node);
    component.push(node);

    for (const neighbor of transpose.get(node) ?? []) {
      if (!visited.has(neighbor)) {
        collect(neighbor, component);
      }
    }
  };

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

    if (!visited.has(node)) {
      const component: T[] = [];
      collect(node, component);
      components.push(component);
    }
  }

  return components;
}

Beispiel: Microservice-Abhängigkeiten

TypeScript
1234567
const services: Graph<string> = new Map([
  ["user", ["payment"]],
  ["payment", ["notification"]],
  ["notification", ["user"]],
  ["report", ["analytics"]],
  ["analytics", ["report"]]
]);

Kosaraju kann unter anderem folgende SCCs erkennen:

Code
12
{user, payment, notification}
{report, analytics}

Damit werden zyklische Abhängigkeiten sichtbar.


Komplexität

Die drei Hauptphasen benötigen jeweils lineare Zeit:

Code
123
DFS 1       O(V + E)
Transpose   O(V + E)
DFS 2       O(V + E)

Insgesamt:

Code
1
O(V + E)

Auch der Speicherbedarf liegt mit Adjazenzlisten in derselben Größenordnung.


Rekursion in JavaScript

Bei sehr tiefen Graphen kann rekursives DFS einen Stack Overflow verursachen:

Code
1
RangeError: Maximum call stack size exceeded

Für sehr große Produktionsgraphen ist daher eine iterative DFS-Implementierung häufig sicherer.


Typische Fehler

Häufige Implementierungsfehler sind:

  • Knoten zu früh in den Finish-Stack einfügen.
  • visited vor dem zweiten DFS nicht zurücksetzen.
  • Den zweiten DFS auf dem Originalgraphen ausführen.
  • Vertices ohne ausgehende Kanten nicht registrieren.
  • Die Reihenfolge innerhalb einer SCC als fest voraussetzen.

Fazit

Kosaraju lässt sich in JavaScript und TypeScript mit einer übersichtlichen Struktur implementieren:

Code
123456789
DFS
↓
Finish Order
↓
Transpose
↓
DFS
↓
SCCs

Mit Adjazenzlisten beträgt die Laufzeit O(V + E).

TypeScript eignet sich besonders für größere Anwendungen, da Generics und Type Safety Graphstrukturen für Module, Packages oder Microservices sauber modellieren können.

Der wichtigste Implementierungsgedanke lautet:

Der erste DFS erzeugt die richtige Reihenfolge, der Transpose Graph kehrt die Erreichbarkeit um und der zweite DFS macht die Grenzen der SCCs sichtbar.

Auf dieser Seite
EinführungGraph mit einer Adjazenzliste darstellenErster DFS-DurchlaufTranspose Graph erstellenVollständige JavaScript-VersionTypeScript mit GenericsBeispiel: Microservice-AbhängigkeitenKomplexitätRekursion in JavaScriptTypische FehlerFazit

Artikeldetails

Veröffentlichungsdaten, Lesezeit und aktuelle Aufrufzahlen.

Veröffentlicht

20. August 2026

Aktualisiert

19. August 2026

Lesezeit

9 Min. Lesezeit

Aufrufe

1

Autor

Arian Soleimanzadeh

Nächster Artikel

B2B CRM vs. B2C CRM: Unterschiede von Vertriebsprozessen bis zur Softwarearchitektur

Lassen Sie uns etwas Klares, Schnelles und Schönes bauen.

Schneller Kontakt für Zusammenarbeit, Beratung oder Produktarbeit.

SchnellkontaktE-Mail senden
Arian Soleimanzadeh

Ein persönliches Portfolio mit Fokus auf moderne Webentwicklung, UI-Systeme und praxisnahe KI-Produkte — sauberer Code, klares Design.

Schnellzugriff

  • Über mich
  • Blog
  • Projekte
  • Kontakt

Kontakt

  • info@ariansoleimanzadeh.site
  • soleimanzadeh.a.work@gmail.com

Verfügbarkeit: Wochentage

Antwortet in der Regel innerhalb von 24 Std.

Newsletter

Erhalten Sie Neuigkeiten zu Beiträgen, Projekten und neuen Veröffentlichungen.

© 2026 ariansoleimanzadeh.site — Alle Rechte vorbehalten.

LinkedIn