Algorithmen und Datenstrukturen in JavaScript (Node.js): vom Anfänger zum Profi
Jeder JavaScript-Entwickler steht früher oder später vor der Notwendigkeit, Algorithmen und Datenstrukturen tiefgehend zu verstehen. Unabhängig davon, ob Sie Frontend mit React oder Backend mit Node.js schreiben, ist das Wissen, wie man Daten effizient organisiert und verarbeitet, eine Schlüsselqualifikation. In diesem Artikel behandeln wir grundlegende Datenstrukturen und gängige Algorithmen mit praktischen Beispielen in JavaScript für die Node.js-Umgebung.
Warum Algorithmen und Datenstrukturen lernen?
Algorithmen und Datenstrukturen sind das Fundament, auf dem jede Anwendung aufbaut. Die richtige Wahl der Datenstruktur kann die Ausführungsgeschwindigkeit eines Programms um ein Vielfaches erhöhen. Beispielsweise kann die Suche nach einem Element in einem Array mit 10.000 Elementen bis zu 10.000 Operationen dauern, während sie in einer Hash-Tabelle nur eine einzige Operation benötigt. Das Verständnis dieser Prinzipien hilft:
- Leistungsfähigeren Code zu schreiben
- Speicher effizient zu nutzen
- Technische Vorstellungsgespräche zu bestehen
- Den inneren Aufbau gängiger Bibliotheken und Frameworks zu verstehen
Grundlegende Datenstrukturen in JavaScript
Arrays
Arrays sind die häufigste Datenstruktur in JavaScript. Sie speichern Elemente in sequenzieller Reihenfolge und ermöglichen den Zugriff über einen Index in O(1). In Node.js sind Arrays besonders nützlich für die Arbeit mit Datenströmen.
// Beispiel für die Arbeit mit Arrays in Node.jsconst numbers = [5, 2, 9, 1, 5, 6];
// Element am Ende hinzufügennumbers.push(10); // [5, 2, 9, 1, 5, 6, 10]
// Letztes Element entfernenconst last = numbers.pop(); // 10
// Element suchenconst index = numbers.findIndex(num => num === 9); // 2
// Filternconst filtered = numbers.filter(num => num > 5); // [9, 6]
console.log(`Ergebnis der Filterung: ${filtered}`);Stack
Ein Stack arbeitet nach dem LIFO-Prinzip (Last In, First Out). Es ist die ideale Struktur für Aufgaben, bei denen der Zugriff auf das zuletzt hinzugefügte Element benötigt wird: Aktionsverlauf (undo/redo), Klammerprüfung, Graphendurchläufe.
class Stack { constructor() { this.items = []; }
push(element) { this.items.push(element); }
pop() { if (this.isEmpty()) return null; return this.items.pop(); }
peek() { if (this.isEmpty()) return null; return this.items[this.items.length - 1]; }
isEmpty() { return this.items.length === 0; }
size() { return this.items.length; }}
// Beispiel: Prüfung auf korrekte Klammerungfunction isValidParentheses(str) { const stack = new Stack(); const pairs = { '(': ')', '{': '}', '[': ']' };
for (let char of str) { if (pairs[char]) { stack.push(char); } else { const last = stack.pop(); if (pairs[last] !== char) return false; } }
return stack.isEmpty();}
console.log(isValidParentheses("({[]})")); // trueconsole.log(isValidParentheses("({[})")); // falseQueue
Eine Queue arbeitet nach dem FIFO-Prinzip (First In, First Out). Sie ist unverzichtbar für die Verarbeitung von Aufgaben in der Reihenfolge ihres Eingangs: Aufgabenplaner, Nachrichtenwarteschlangen, BFS (Breitensuche).
class Queue { constructor() { this.items = {}; this.front = 0; this.rear = 0; }
enqueue(element) { this.items[this.rear] = element; this.rear++; }
dequeue() { if (this.isEmpty()) return null; const item = this.items[this.front]; delete this.items[this.front]; this.front++; return item; }
isEmpty() { return this.front === this.rear; }
size() { return this.rear - this.front; }}
// Beispiel: Simulation einer Druckwarteschlangeconst printQueue = new Queue();printQueue.enqueue("document1.pdf");printQueue.enqueue("document2.pdf");printQueue.enqueue("photo.jpg");
while (!printQueue.isEmpty()) {
```