📦 Datenstrukturen – der Werkzeugkasten
Daten passend organisieren, typische Operationen verstehen und für jedes Problem das geeignete Werkzeug auswählen – mit Code, Vergleichen und interaktivem Labor.
🛠️ Empfohlene Tools für Entwickler
Diese Tools nutze ich selbst – sie helfen Ihnen, Ihre Projekte professionell umzusetzen und zu vermarkten.
⚡ ExeOutput for PHP
PHP-Anwendungen in eigenständige Windows-EXE-Dateien kompilieren – ideal für die Auslieferung Ihrer Datenstruktur-basierten Anwendungen.
Jetzt ansehen →📄 HTML Executable
HTML-Dateien und Websites als eigenständige Windows-EXE-Dateien kompilieren.
Jetzt ansehen →🔒 XLS Padlock
Excel-Arbeitsmappen als EXE kompilieren und schützen – perfekt für datenbank-gestützte Excel-Lösungen mit komplexen Datenstrukturen.
Jetzt ansehen →🐍 VBA Padlock
VBA-Code kompilieren und Office-Lösungen verkaufen – ohne Source-Code-Weitergabe.
Jetzt ansehen →📦 Paquet Builder
Professionelle Windows-Installationsprogramme für Ihre Anwendungen erstellen.
Jetzt ansehen →* Affiliate-Links: Bei einem Kauf über diese Links erhalten wir eine kleine Provision – für Sie entstehen keine zusätzlichen Kosten.
1. Was ist eine Datenstruktur?
Eine Datenstruktur organisiert Werte und legt fest, welche Operationen sinnvoll möglich sind. Sie beeinflusst, wie leicht Daten gespeichert, gefunden, verändert, durchlaufen und gelöscht werden können.
Organisation
Daten werden nach einer bestimmten Ordnung, Beziehung oder Zugriffsidee angeordnet.
Operationen
Jede Struktur bietet typische Aktionen wie Lesen, Suchen, Einfügen oder Entfernen.
Kosten
Dieselbe Operation kann je nach Struktur konstant, logarithmisch oder linear wachsen.
Datenstruktur und Algorithmus
Ein Algorithmus beschreibt, wie ein Problem gelöst wird. Eine Datenstruktur bestimmt, wie die benötigten Informationen organisiert sind. Beide gehören zusammen: Eine passende Struktur kann einen Algorithmus vereinfachen und beschleunigen.
2. Abstrakter Datentyp und konkrete Umsetzung
Ein abstrakter Datentyp beschreibt erlaubte Werte und Operationen, ohne die interne Speicherung festzulegen. Eine konkrete Datenstruktur setzt dieses Verhalten technisch um.
Schnittstelle
Legt fest, welche Operationen verfügbar sind und welches Verhalten sie besitzen.
Implementierung
Entscheidet, wie Werte intern angeordnet werden und welche Kosten entstehen.
3. Lineare und nichtlineare Strukturen
Linear
Elemente erscheinen in einer Folge. Typische Beispiele sind Array, verkettete Liste, Stack und Queue.
Hierarchisch
Elemente besitzen Eltern-Kind-Beziehungen. Ein Baum kann etwa Ordner oder Kategorien darstellen.
Vernetzt
Graphen bilden beliebige Beziehungen zwischen Knoten ab, zum Beispiel Straßen oder Abhängigkeiten.
Weitere wichtige Eigenschaften
- Geordnet oder ungeordnet
- Eindeutige Werte oder Duplikate
- Feste oder dynamische Größe
- Direkter Indexzugriff oder schrittweises Durchlaufen
- Veränderlich oder unveränderlich
4. Array und dynamische Liste
Ein klassisches Array speichert Elemente in einer geordneten, zusammenhängenden Struktur und ermöglicht direkten Zugriff über einen Index. Viele Hochsprachen bieten zusätzlich dynamische Arrays, deren Kapazität bei Bedarf wächst.
Indexbasierter Zugriff
Wert: A B C D
Die Position eines Elements kann aus dem Index direkt bestimmt werden. Einfügen am Anfang oder in der Mitte verschiebt dagegen häufig nachfolgende Elemente.
Stärken
- Schneller Indexzugriff
- Gute Speicherlokalität
- Einfaches Durchlaufen
- Viele eingebaute Sprachoperationen
Grenzen
- Einfügen und Löschen in der Mitte meist linear
- Suche ohne zusätzliche Ordnung meist linear
- Klassische Arrays besitzen feste Größe
- Wachstum dynamischer Arrays kann gelegentlich Kopieren erfordern
O(1). Einzelne Wachstumsschritte können O(n) kosten, sind aber nicht bei jedem Anhängen nötig.
5. Verkettete Liste
Eine verkettete Liste besteht aus Knoten. Jeder Knoten speichert einen Wert und einen Verweis auf den nächsten Knoten; bei einer doppelt verketteten Liste zusätzlich auf den vorherigen.
null
Stärken
- Dynamische Anzahl von Knoten
- Einfügen nach bekanntem Knoten in O(1)
- Kein Verschieben nachfolgender Elemente
Grenzen
- Indexzugriff benötigt Durchlaufen: O(n)
- Zusätzlicher Speicher für Verweise
- Schlechtere Speicherlokalität
O(n) hinzu.
6. Stack – Last In, First Out
Ein Stack arbeitet nach dem LIFO-Prinzip: Das zuletzt abgelegte Element wird zuerst entfernt. Der Zugriff erfolgt normalerweise nur an der Oberseite.
push
typisch O(1)Legt ein Element oben auf den Stack.
pop
typisch O(1)Entfernt und liefert das oberste Element.
peek
O(1)Liest das oberste Element, ohne es zu entfernen.
Typische Anwendungen
- Rückgängig-Funktion in Editoren
- Funktionsaufrufe und Rekursion
- Prüfung verschachtelter Klammern
- Tiefensuche in Bäumen und Graphen
- Zurück-Navigation in einem Ablauf
FÜR jedes zeichen IN text
WENN zeichen eine öffnende Klammer ist DANN
stack.push(zeichen)
SONST WENN zeichen eine schließende Klammer ist DANN
WENN stack leer ODER oberste Klammer passt nicht DANN
RÜCKGABE falsch
ENDE WENN
stack.pop()
ENDE WENN
ENDE FÜR
RÜCKGABE stack ist leer
7. Queue und Deque
Eine Queue arbeitet typischerweise nach FIFO: Das zuerst eingereihte Element wird zuerst entfernt. Eine Deque erlaubt Einfügen und Entfernen an beiden Enden.
Queue
Druckaufträge, Nachrichten, Aufgabenplanung und Breitensuche folgen häufig einer Warteschlangenidee.
Deque
Gleitende Fenster, Verlaufspuffer und Algorithmen mit Zugriff auf beide Enden profitieren von einer Deque.
Array.shift() entfernt das erste Element, verschiebt dabei aber typischerweise die übrigen Elemente und kostet O(n). Für große oder stark genutzte Queues ist eine Kopfindex- oder Deque-Implementierung geeigneter.
8. Hash Map und Set
Eine Map ordnet eindeutigen Schlüsseln Werte zu. Ein Set speichert eindeutige Werte ohne Duplikate. Hash-basierte Implementierungen berechnen aus einem Schlüssel eine Position in einer internen Tabelle.
Map / Dictionary
Geeignet für Nachschlagen über einen Schlüssel, Zähler und Gruppierungen.
Set / Menge
Geeignet für Eindeutigkeit, Duplikatentfernung und schnelle Mitgliedschaftstests.
Hashfunktion
Gute Verteilung und Kollisionsbehandlung beeinflussen die praktische Leistung.
Kollisionen
Unterschiedliche Schlüssel können auf dieselbe Tabellenposition abgebildet werden. Implementierungen lösen solche Kollisionen beispielsweise durch Verkettung oder offene Adressierung.
O(1) bei Hash Maps und Sets ist typischerweise eine durchschnittliche beziehungsweise amortisierte Aussage. Im ungünstigsten Fall können Operationen linear werden. Details hängen von Implementierung, Hashfunktion und Eingaben ab.
9. Bäume, Suchbäume und Heap
Ein Baum organisiert Knoten hierarchisch. Der oberste Knoten heißt Wurzel; Knoten können Kinder besitzen. Ein Knoten ohne Kinder wird häufig Blatt genannt.
Allgemeiner Baum
Stellt Hierarchien wie Dateisystem, Kategorien oder das HTML-DOM dar.
Binärer Suchbaum
Ordnet kleinere und größere Schlüssel. Bei ausgeglichener Höhe sind typische Operationen O(log n).
Heap
Hält Minimum oder Maximum an der Spitze und eignet sich zur Umsetzung einer Prioritätswarteschlange.
🎯 Live-Demo: Heap (Prioritätswarteschlange)
O(log n) auf O(n).
10. Trie – Präfixbaum für Text
Ein Trie (auch Präfixbaum) speichert Zeichenketten in einer Baumstruktur. Jeder Knoten repräsentiert ein Zeichen, und der Pfad von der Wurzel zu einem Knoten bildet ein Präfix. Er eignet sich besonders für Auto-Vervollständigung, Rechtschreibprüfung und Wörterbuch-Suchen.
Präfix-Suche
Der Trie findet Wörter, die mit einem bestimmten Präfix beginnen – sehr nützlich für Suchvorschläge.
🎯 Live-Demo: Trie (Präfixbaum)
11. Graphen
Ein Graph besteht aus Knoten und Kanten. Kanten können gerichtet oder ungerichtet sowie mit einem Gewicht versehen sein.
Anwendungsfälle
- Straßen und Routen
- Soziale Netzwerke
- Abhängigkeiten
- Computernetze
Durchlaufen
- Breitensuche mit Queue
- Tiefensuche mit Stack oder Rekursion
- Kürzeste Wege
- Topologische Sortierung
Adjazenzliste
graph = {
"Köln": ["Bonn", "Essen"],
"Bonn": ["Köln", "Aachen"],
"Essen": ["Köln", "Aachen"],
"Aachen": ["Bonn", "Essen"]
}
Eine Adjazenzliste speichert zu jedem Knoten seine Nachbarn. Bei dünn besetzten Graphen benötigt sie häufig weniger Speicher als eine vollständige Adjazenzmatrix.
12. Bloom-Filter – probabilistische Struktur
Ein Bloom-Filter ist eine speicher-effiziente, probabilistische Datenstruktur. Er prüft, ob ein Element möglicherweise in einer Menge enthalten ist. Falsch-Positiv ist möglich, aber Falsch-Negativ nicht.
Probabilistische Mitgliedschaft
Speicher: O(1) · Zeit: O(k)Einsatzgebiete: Cache-Entscheidungen, Datenbanksysteme, Web-Crawler, Spam-Filter.
🎯 Live-Demo: Bloom-Filter
13. LRU-Cache
Ein LRU-Cache (Least Recently Used) speichert eine begrenzte Anzahl von Elementen. Wenn die Kapazität erreicht ist, wird das am längsten nicht verwendete Element entfernt. Er kombiniert eine Hash-Map für schnellen Zugriff mit einer doppelt verketteten Liste für die Reihenfolge.
Cache mit Verdrängung
Zugriff: O(1) · Einfügen: O(1)Einsatzgebiete: Datenbank-Caches, Browser-Cache, Speicherverwaltung.
🎯 Live-Demo: LRU-Cache
14. Disjoint Set (Union-Find)
Ein Disjoint Set (auch Union-Find) verwaltet eine Sammlung von disjunkten Mengen. Er unterstützt zwei Operationen: Union (zwei Mengen zusammenführen) und Find (die Menge eines Elements finden).
Mengen verwalten
Find mit Pfadkomprimierung: nahezu O(1)Einsatzgebiete: Graphenalgorithmen (Kruskal), Bildverarbeitung, Netzwerk-Konnektivität.
🎯 Live-Demo: Disjoint Set (Union-Find)
15. Operationen und typische Laufzeiten
Eine pauschale Bewertung als „schnell“ oder „langsam“ reicht nicht. Entscheidend sind konkrete Operation, Implementierung und Voraussetzungen.
| Struktur | Index / Schlüssel lesen | Suchen | Am Ende einfügen | In der Mitte einfügen | Bemerkung |
|---|---|---|---|---|---|
| Dynamisches Array | O(1) | O(n) | amortisiert O(1) | O(n) | Indexzugriff und gute Speicherlokalität |
| Verkettete Liste | O(n) | O(n) | O(1) mit Endzeiger | O(1) nach bekanntem Knoten | Suche nach Position kann zusätzlich O(n) kosten |
| Hash Map | durchschnittlich O(1) | über Schlüssel durchschnittlich O(1) | durchschnittlich O(1) | nicht anwendbar | Ungünstigster Fall kann O(n) sein |
| Ausgeglichener Suchbaum | über Schlüssel O(log n) | O(log n) | O(log n) | O(log n) | Hält eine Schlüsselordnung aufrecht |
| Stack | Spitze O(1) | O(n) | push typischerweise O(1) | nicht Teil der Schnittstelle | LIFO-Verhalten |
| Queue / Deque | Enden O(1) | O(n) | typischerweise O(1) | nicht Teil der Schnittstelle | Hängt von geeigneter Implementierung ab |
| Trie | über Präfix O(l) | O(l) | O(l) | nicht anwendbar | l = Länge des Wortes |
| Bloom-Filter | nicht anwendbar | O(k) | O(k) | nicht anwendbar | k = Anzahl Hash-Funktionen |
| LRU-Cache | O(1) | O(1) | O(1) | nicht anwendbar | Kombiniert Map + Liste |
| Disjoint Set | Find nahezu O(1) | nicht anwendbar | Union nahezu O(1) | nicht anwendbar | Mit Pfadkomprimierung |
16. Datenstrukturen in Python
Liste, Dictionary und Set
# Dynamische Liste
technologien = ["HTML", "CSS", "Python"]
technologien.append("SQL")
# Dictionary: Schlüssel → Wert
profil = {"name": "Murat", "erfahrung": 15}
profil["stadt"] = "Köln"
# Set: eindeutige Werte
tags = {"Web", "Python", "Web"}
print(tags) # enthält "Web" nur einmal
Effiziente Queue mit deque
from collections import deque
auftraege = deque(["A-101", "A-102"])
auftraege.append("A-103") # hinten einreihen
naechster = auftraege.popleft() # vorne entnehmen
print(naechster) # A-101
Häufigkeiten mit einem Dictionary zählen
woerter = ["python", "web", "python", "sql", "web", "python"]
haeufigkeit = {}
for wort in woerter:
haeufigkeit[wort] = haeufigkeit.get(wort, 0) + 1
print(haeufigkeit)
# {'python': 3, 'web': 2, 'sql': 1}
17. Datenstrukturen in JavaScript
Array, Map und Set
// Dynamisches Array
const technologien = ["HTML", "CSS", "JavaScript"];
technologien.push("SQL");
// Map: Schlüssel dürfen verschiedene Typen besitzen
const profil = new Map();
profil.set("name", "Murat");
profil.set("erfahrung", 15);
// Set: eindeutige Werte
const tags = new Set(["Web", "Python", "Web"]);
console.log(tags.size); // 2
Queue mit Kopfindex
class Queue {
constructor() {
this.values = [];
this.head = 0;
}
enqueue(value) {
this.values.push(value);
}
dequeue() {
if (this.isEmpty()) return undefined;
const value = this.values[this.head];
this.head += 1;
return value;
}
isEmpty() {
return this.head >= this.values.length;
}
}
shift() vermieden werden kann.
18. Interaktives Datenstruktur-Labor
Wähle eine Struktur und führe typische Operationen aus. Das Labor zeigt, wie sich Reihenfolge, Duplikate und Zugriffspunkte unterscheiden.
Struktur und Werte
19. Die passende Datenstruktur auswählen
Beginne nicht mit dem Namen einer Struktur, sondern mit den benötigten Operationen und fachlichen Regeln.
- 1Zugriff bestimmenWird per Position, Schlüssel, Priorität, Reihenfolge oder Beziehung zugegriffen?
- 2Ordnung klärenMuss Einfügereihenfolge, Sortierung oder Hierarchie erhalten bleiben?
- 3Duplikate prüfenSind gleiche Werte erlaubt oder muss jedes Element eindeutig sein?
- 4Änderungsmuster betrachtenWird häufig gelesen, gesucht, am Ende ergänzt oder in der Mitte verändert?
- 5Datenmenge und Speicher bewertenWelche Größen und Wachstumsszenarien sind realistisch?
- 6Einfach starten und messenDie verständlichste passende Struktur wählen und nur bei messbarem Bedarf optimieren.
| Anforderung | Geeigneter Startpunkt | Beispiel |
|---|---|---|
| Geordnete Folge mit Indexzugriff | Dynamisches Array / Liste | Produkte in sichtbarer Reihenfolge |
| Schlüssel schnell nachschlagen | Map / Dictionary | Kundennummer → Kundendaten |
| Eindeutige Werte | Set | Bereits verarbeitete IDs |
| Zuletzt hinzugefügt zuerst | Stack | Rückgängig-Verlauf |
| Zuerst hinzugefügt zuerst | Queue | Auftragsbearbeitung |
| Immer höchste Priorität zuerst | Prioritätsqueue / Heap | Dringende Aufgaben |
| Präfix-Suche | Trie | Auto-Vervollständigung |
| Probabilistische Mitgliedschaft | Bloom-Filter | Cache-Entscheidungen |
| Cache mit Verdrängung | LRU-Cache | Datenbank-Cache |
| Mengen zusammenführen | Disjoint Set | Graph-Algorithmen |
| Hierarchie | Baum | Kategorien und Ordner |
| Beliebige Beziehungen | Graph | Routen oder Abhängigkeiten |
20. Checkliste für die Wahl einer Datenstruktur
Hake die erledigten Punkte ab. Die Auswahl bleibt nur während dieses Seitenaufrufs erhalten.
0 von 10 Punkten erledigt
21. Häufige Fehler und Lösungen
Nur nach Durchschnittslaufzeit entscheiden
Problem: Ungünstige Fälle, Speicher und Reihenfolge werden übersehen.
Lösung: Anforderungen, Worst Case und Implementierungsdetails gemeinsam bewerten.
Liste als große Queue verwenden
Problem: Wiederholtes Entfernen am Anfang kann alle übrigen Elemente verschieben.
Lösung: Deque, Ringpuffer oder Kopfindex verwenden.
Map und Objekt gleichsetzen
Problem: Schlüsseltypen, Prototypen und API unterscheiden sich je nach Sprache.
Lösung: Für echte Schlüssel-Wert-Sammlungen die vorgesehene Map-Struktur prüfen.
Vorzeitig eine komplexe Struktur bauen
Problem: Mehr Code und Fehlerquellen entstehen ohne messbaren Nutzen.
Lösung: Mit der einfachsten passenden Bibliotheksstruktur beginnen und messen.
Bloom-Filter als exakte Struktur missverstehen
Problem: Falsch-Positive werden nicht berücksichtigt.
Lösung: Bloom-Filter nur verwenden, wenn Falsch-Positive akzeptabel sind oder zur Optimierung vor einer exakten Prüfung.
22. Übungen zum Mitmachen
Löse jede Aufgabe zuerst mit einer Begründung für die gewählte Struktur.
Duplikate entfernen
Entferne doppelte Technologie-Namen und begründe, wann ein Set geeignet ist.
Aufträge bearbeiten
Simuliere fünf Aufträge in Eingangsreihenfolge mit einer Queue.
Wörter zählen
Erstelle eine Häufigkeitstabelle für Wörter mit einer Map beziehungsweise einem Dictionary.
Klammern prüfen
Verwende einen Stack, um ({[]}) und ([)] zu prüfen.
LRU-Cache implementieren
Implementiere einen LRU-Cache mit einer Map und einer doppelt verketteten Liste.
Trie für Auto-Vervollständigung
Baue einen Trie mit Wörtern und implementiere eine Methode, die alle Wörter mit einem Präfix zurückgibt.
Lösungshinweise
Hinweis zu Übung 1 anzeigen
Füge alle Werte in ein Set ein. Soll die ursprüngliche Reihenfolge erhalten bleiben, prüfe das Verhalten der Set-Implementierung oder baue das Ergebnis beim Durchlaufen bewusst auf.
Hinweis zu Übung 2 anzeigen
Jeder neue Auftrag wird mit enqueue hinten eingereiht; dequeue liefert jeweils den ältesten noch offenen Auftrag.
Hinweis zu Übung 3 anzeigen
Nutze das Wort als Schlüssel und erhöhe den zugehörigen Zähler bei jedem Vorkommen.
Hinweis zu Übung 4 anzeigen
Lege öffnende Klammern auf den Stack. Bei einer schließenden Klammer muss die oberste öffnende Klammer zum Typ passen.
Hinweis zu Übung 5 anzeigen
Verwende eine Map für den O(1)-Zugriff und eine doppelt verkettete Liste für die Reihenfolge. Bei Zugriff oder Einfügen das Element an den Anfang bewegen.
Hinweis zu Übung 6 anzeigen
Jeder Knoten im Trie hat ein Dictionary für die Kinder und ein Flag, ob das Wort endet. Die Präfix-Suche traversiert den Baum und sammelt alle Wörter unter dem gefundenen Knoten.
23. Passende nächste Themen
🧮 Algorithmen verstehen
Such-, Sortier- und Traversierungsverfahren mit passenden Strukturen verbinden.
Weiterlernen →🐍 Python für Einsteiger
Listen, Dictionaries, Sets und Deques praktisch einsetzen.
Weiterlernen →🟨 JavaScript für Einsteiger
Arrays, Objects, Map und Set in Web-Anwendungen verwenden.
Weiterlernen →🗄️ Datenbanken verstehen
Dauerhafte Speicherung, Indizes, Tabellen und Beziehungen kennenlernen.
Weiterlernen →24. Häufige Fragen zu Datenstrukturen
Was ist der Unterschied zwischen Array und Liste?
Ein klassisches Array besitzt feste Größe und zusammenhängenden Speicher. „Liste“ kann je nach Sprache ein dynamisches Array, eine verkettete Liste oder einen abstrakten Datentyp meinen. Python-Listen und JavaScript-Arrays verhalten sich in vielen Alltagsfällen wie dynamische Arrays.
Wann ist eine Map besser als eine Liste?
Wenn Werte hauptsächlich über eindeutige Schlüssel statt über Positionen gefunden werden, ist eine Map oft natürlicher. Für eine geordnete Folge und Indexzugriff ist eine Liste meist passender.
Was ist der Unterschied zwischen Stack und Queue?
Ein Stack arbeitet nach LIFO: zuletzt hinein, zuerst heraus. Eine Queue arbeitet nach FIFO: zuerst hinein, zuerst heraus.
Warum sind Hash Maps nicht immer O(1)?
O(1) beschreibt typischerweise den durchschnittlichen oder amortisierten Fall. Kollisionen, Größenänderungen und ungünstige Eingaben können einzelne oder im schlechtesten Fall viele Operationen verteuern.
Ist eine verkettete Liste immer besser beim Einfügen?
Nein. Einfügen ist nur dann konstant, wenn die Stelle beziehungsweise der betreffende Knoten bereits bekannt ist. Das Suchen der Position kann linear kosten. Arrays profitieren außerdem oft von guter Speicherlokalität.
Wofür braucht man einen Heap?
Ein Heap ermöglicht schnellen Zugriff auf das kleinste oder größte Element und wird häufig zur Umsetzung einer Prioritätsqueue verwendet. Er ist nicht dasselbe wie der allgemeine Speicherbereich, der ebenfalls „Heap“ genannt wird.
Was ist der Unterschied zwischen Baum und Graph?
Ein Baum ist ein spezieller hierarchischer Graph ohne Zyklen und mit eindeutigem Pfad von der Wurzel zu jedem Knoten. Allgemeine Graphen können mehrere Wege, Zyklen und beliebige Beziehungen besitzen.
Was ist ein Bloom-Filter?
Ein Bloom-Filter ist eine speichereffiziente, probabilistische Datenstruktur, die testet, ob ein Element möglicherweise in einer Menge enthalten ist. Falsch-Positive sind möglich, Falsch-Negative nicht.
Wann verwendet man einen Trie?
Ein Trie eignet sich für alle Operationen mit Präfixen: Auto-Vervollständigung, Rechtschreibprüfung, Wörterbücher und Textanalyse.
Was ist ein LRU-Cache?
Ein LRU-Cache speichert eine begrenzte Anzahl von Elementen und entfernt bei Erreichen der Kapazität das am längsten nicht verwendete Element (Least Recently Used).
Hinweis: Laufzeiten hängen von konkreter Implementierung und Voraussetzungen ab. Prüfe die Dokumentation deiner Programmiersprache und miss mit realistischen Daten.