Bäume
Wie funktionieren Bäume und binäre Suchbäume?
Von der Liste zur Hierarchie: Das Konzept des Baums
Bisher hast du Datenstrukturen wie Arrays, Listen, Stacks oder Queues kennengelernt. Diese sind linear: Ein Element folgt auf das nächste, wie Perlen auf einer Schnur. In der Realität sind Daten aber oft hierarchisch strukturiert. Denk an das Dateisystem auf deinem Computer (Ordner in Ordnern), die Struktur einer HTML-Webseite oder den Stammbaum deiner Familie.
Um solche Beziehungen abzubilden, nutzen wir in der Informatik Bäume. Ein Baum besteht aus Knoten (Nodes), die Daten speichern, und Kanten (Edges), die die Verbindungen darstellen.
- Wurzel (Root): Der allererste Knoten ganz oben (z. B. das Laufwerk
C:\). Er hat keinen Vorgänger. - Eltern (Parent) & Kinder (Child): Ein Knoten, der auf andere verweist, ist der Elternknoten. Die darunterliegenden sind seine Kinder.
- Blätter (Leaves): Knoten, die keine Kinder mehr haben (z. B. Dateien in einem Ordner). Sie bilden das Ende eines Pfades.
Technisch gesehen ist ein Baum eine rekursive Struktur. Jeder Knoten kann als Wurzel eines eigenen kleinen "Teilbaums" betrachtet werden.
// Pseudocode: Ein allgemeiner Baum-Knoten
Klasse TreeNode:
Daten wert
Liste<TreeNode> kinder // Ein Knoten kann beliebig viele Kinder habenDer Binärbaum: Die Macht der Zwei
Ein Binärbaum ist eine spezialisierte Form des Baums, die in der Informatik extrem häufig vorkommt. Die Regel ist strikt: Jeder Knoten darf höchstens zwei Kinder haben:
- Das linke Kind
- Das rechte Kind
Diese Einschränkung macht die Verwaltung der Daten sehr effizient und die Algorithmen überschaubar. Da wir genau wissen, dass es maximal zwei Nachfolger gibt, müssen wir keine dynamische Liste von Kindern verwalten, sondern nur zwei Verweise (Pointer).
// Pseudocode: Ein Binärbaum-Knoten
Klasse BinaryNode:
Daten wert
BinaryNode links // Verweis auf das linke Kind (oder NULL)
BinaryNode rechts // Verweis auf das rechte Kind (oder NULL)Wege durch den Wald: Traversierung
Da Bäume nicht linear sind (es gibt kein einfaches "Vorne" und "Hinten"), stellt sich die Frage: In welcher Reihenfolge verarbeite ich alle Knoten, wenn ich z. B. alle Daten ausgeben will? Diesen Vorgang nennt man Traversierung. Bei Binärbäumen unterscheiden wir drei Hauptwege, die sich durch den Zeitpunkt definieren, wann der Elternknoten (Wurzel des Teilbaums) verarbeitet wird:
- Preorder (Wurzel -> Links -> Rechts):
- Du verarbeitest zuerst den Knoten selbst, dann steigst du in den linken und danach in den rechten Teilbaum ab.
- Anwendung: Ideal, um eine Kopie des Baums zu erstellen, da die Struktur von oben nach unten erhalten bleibt.
- Inorder (Links -> Wurzel -> Rechts):
- Du gehst erst so tief wie möglich nach links, verarbeitest dann den Knoten und gehst dann nach rechts.
- Anwendung: Bei sortierten Bäumen liefert dies die Werte aufsteigend sortiert.
- Postorder (Links -> Rechts -> Wurzel):
- Du verarbeitest erst beide Kinder komplett, bevor du dich um den Elternknoten kümmerst.
- Anwendung: Perfekt zum Löschen eines Baums. Du löschst erst die Kinder (Blätter), bevor du den Elternknoten entfernst, damit keine Referenzen verloren gehen.
// Beispiel: Inorder-Traversierung als rekursive Funktion
Funktion inOrder(knoten):
IF knoten IS NULL THEN RETURN // Abbruchbedingung: Ende des Astes
inOrder(knoten.links) // 1. Erst links absteigen
print(knoten.wert) // 2. Dann aktuellen Wert verarbeiten
inOrder(knoten.rechts) // 3. Dann rechts absteigenWie funktioniert der binäre Suchbaum (BST)?
Ordnung muss sein
Ein normaler Binärbaum hat keine Regeln, wo welche Daten liegen. Ein binärer Suchbaum (Binary Search Tree - BST) hingegen folgt einer strengen Logik, die ihn zu einem der effizientesten Werkzeuge für die Datensuche macht:
- Alle Werte im linken Teilbaum sind kleiner als der Elternknoten.
- Alle Werte im rechten Teilbaum sind größer als der Elternknoten.
- (Duplikate werden je nach Implementierung oft ausgeschlossen oder konsequent auf eine Seite gepackt).
Stell dir vor, die Wurzel hat den Wert 50. Alle Knoten links davon müssen kleiner als 50 sein (z. B. 20, 10, 45), alle rechts davon größer (z. B. 70, 60, 90).
Warum ist ein BST nützlich?
Der BST funktioniert wie das Spiel "Zahlenraten" ("Ich denke mir eine Zahl zwischen 1 und 100"). Wenn du 15 in einem BST suchst und an der Wurzel 50 stehst, weißt du sofort: "15 ist kleiner als 50". Du musst den gesamten rechten Teil des Baumes (alles größer 50) gar nicht erst ansehen. Du gehst nach links.
Mit jedem Schritt halbiert sich (im Idealfall eines balancierten Baums) die Menge der zu durchsuchenden Daten.
- Listen: Um einen Wert zu finden, musst du im schlimmsten Fall alle Elemente ansehen (O(n)).
- BST: Du musst nur einen Pfad von der Wurzel zum Blatt gehen (O(log n)). Bei 1.000.000 Einträgen brauchst du in einer Liste bis zu 1 Mio. Schritte, im BST nur ca. 20.
// Pseudocode: Suche im BST
Funktion suche(knoten, gesuchterWert):
// Fall 1: Baum leer oder Ende erreicht -> Nicht gefunden
IF knoten IS NULL THEN RETURN "Nicht gefunden"
// Fall 2: Volltreffer
IF gesuchterWert == knoten.wert THEN RETURN "Gefunden!"
// Fall 3: Wert ist kleiner -> Gehe nach links
IF gesuchterWert < knoten.wert THEN
RETURN suche(knoten.links, gesuchterWert)
// Fall 4: Wert ist größer -> Gehe nach rechts
ELSE
RETURN suche(knoten.rechts, gesuchterWert)Wann wird der Baum zur Linie? (Das Problem der Balance)
Du hast gelernt, dass ein binärer Suchbaum (BST) extrem effizient ist, weil er bei jeder Entscheidung einen großen Teil der Daten ausschließt (O(log n)). Es gibt jedoch einen Worst Case: Wenn du Daten, die bereits sortiert sind (z. B. 1, 2, 3, 4, 5), nacheinander in einen leeren BST einfügst, entsteht kein verzweigter Baum, sondern eine lange, dünne Linie. Da jeder neue Wert größer ist als der vorherige, landet er immer rechts unten. In der Fachsprache nennt man dies einen entarteten Baum; er verhält sich technisch wie eine langsame, verkettete Liste.
Wenn du in diesem "Baum" die 5 suchst, musst du jeden einzelnen Knoten besuchen. Die Effizienz sinkt dramatisch auf O(n). Damit ein Suchbaum seine Geschwindigkeit behält, muss er balanciert sein. Ein Baum gilt als balanciert, wenn sich die Höhe des linken und des rechten Teilbaums bei jedem Knoten höchstens um 1 unterscheidet. In der Praxis nutzen Systeme daher oft selbst-balancierende Bäume, die ihre Struktur bei jedem Einfügen automatisch korrigieren ("rotieren"), um so flach und effizient wie möglich zu bleiben.
Lernziele
- das Konzept von Bäumen erklären, indem die grundlegenden Eigenschaften von Bäumen (hierarchische Struktur, Knoten, Kanten, Wurzel, Blätter) und ihre Unterschiede zu anderen Datenstrukturen wie Arrays und Listen erläutert werden.
- das Konzept von Binärbäumen erklären, indem die speziellen Eigenschaften von Binärbäumen (höchstens zwei Kindknoten pro Knoten, rekursive Struktur) und ihre grundlegende Verwendung zur effizienten Speicherung und Suche von Daten erläutert werden.
- die Funktionsweise eines binären Suchbaums (BST) erklären, indem die Ordnungsregel (linkes Kind < Elternknoten < rechtes Kind) und deren Vorteil für effiziente Suchoperationen (im Durchschnitt O(log n)) erläutert wird.
- die Traversierung von Bäumen veranschaulichen, indem die verschiedenen Traversierungsarten (Preorder, Inorder, Postorder) anhand von Beispielen dargestellt und ihre jeweiligen Anwendungsfälle erläutert werden.
Vertiefe dein Wissen!
Du hast die Grundlagen verstanden? Perfekt! In unserer App findest du interaktive Übungen, praktische Projekte und erweiterte Inhalte zu Bäume.
Ab 5€/Monat • Kostenloser Test verfügbar