Trie: Der Präfixbaum Schritt für Schritt

Sechzehn Wörter stehen in einem Baum, mit einem Buchstaben pro Knoten. Wörter mit gleichem Anfang teilen sich den entsprechenden Pfad: car, card und cart führen über dieselben Knoten c, a, r. Ein doppelter Ring kennzeichnet einen Knoten, an dem ein Wort endet. Gib ein Wort ein, wähle eine Operation und geh sie Schritt für Schritt durch.

Interaktive Simulation wird geladen...

Lektion

Die Theorie — Trie: Der Präfixbaum Schritt für Schritt

Ein Trie, auch Präfixbaum genannt, speichert eine Menge von Wörtern in einem Baum. Für jedes unterschiedliche Präfix gibt es einen Knoten. Die Wurzel steht für das leere Präfix; jede Kante trägt einen Buchstaben. Der Pfad von der Wurzel zu einem Knoten ergibt somit dessen Präfix. Ein Knoten ist markiert, wenn sein Präfix ein vollständiges gespeichertes Wort ist. Wörter mit gleichem Anfang teilen sich den Pfad für diesen Anfang. Bei einer Wortsuche folgst du den Buchstaben des Wortes von der Wurzel aus. Bei einer Präfixsuche gehst du zum Knoten des Präfixes und sammelst alle markierten Knoten darunter. Edward Fredkin gab der Struktur 1960 ihren Namen, abgeleitet von retrieval. Er sah die Aussprache wie beim englischen Wort tree vor.

Was die einzelnen Symbole bedeuten

W
Anzahl der Wörter in der Liste
L
Gesamtzahl der Buchstaben aller Wörter
N
Anzahl der Knoten, einer pro unterschiedlichem Präfix, ohne die Wurzel
Setzt voraus
Wir verwenden Kleinbuchstaben von a bis z, ein Byte pro Buchstabe und 64-Bit-Zeiger. In beiden Strukturen zählen wir weder Füllbytes zur Speicherausrichtung noch Verwaltungsaufwand der Speicherverwaltung mit. Bei der binären Suche zählt jedes verglichene Buchstabenpaar. Der Vergleich endet beim ersten Unterschied; das Erreichen eines Wortendes verursacht keine zusätzlichen Kosten. Für die Hashtabelle nehmen wir den günstigsten Fall an: keine Kollisionen. Im 18-Byte-Knoten sind die Kindknoten alphabetisch geordnet. Die Suche unter ihnen endet daher, sobald sie den gesuchten Buchstaben überschritten hat.

Die binäre Suche liest c, a, r jeweils 4-mal, der Trie nur einmal 🖖

Klick auf car suchen. Der Trie folgt in jedem Schritt einem Buchstaben und findet car als Wort markiert: 3 Buchstaben. Die binäre Suche in denselben 16 sortierten Wörtern prüft catch, careful, card und car. Alle beginnen mit ca. Jeder Vergleich muss deshalb 3 Buchstaben lesen, bevor die Suchrichtung feststeht: insgesamt 12. Eine Hashtabelle liest die 3 Buchstaben, um einen Tabellenplatz zu berechnen, und weitere 3, um das dort gefundene Wort zu prüfen: 6. Bei der Suche nach allen 16 gespeicherten Wörtern liest der Trie insgesamt 70 Buchstaben, die Hashtabelle 140 und die binäre Suche 169. Die gemeinsamen Wortanfänge sparen dem Trie Knoten, verlängern aber die Vergleiche bei der binären Suche. Speichere alle 17.576 Zeichenfolgen aus je 3 Buchstaben, von aaa bis zzz: Dann braucht die binäre Suche im Mittel 26,4 gelesene Buchstaben, um eine davon zu finden. Der Trie liest weiterhin 3.

Auch eine sortierte Liste kann Wörter vervollständigen. Eine Hashtabelle muss jedes Wort lesen 🖖

Klick auf Wörter mit car. Der Trie folgt c, a, r und besucht danach die 8 Knoten unter car: insgesamt 11 Knoten, von denen 6 ein Wortende markieren. Dog, plan und die übrigen Wörter bleiben unberührt. Auch in einer sortierten Liste lässt sich die Suche auf die passenden Wörter beschränken, denn alle Wörter mit dem Anfang car stehen nebeneinander. Die binäre Suche findet das erste davon mit 5 Suchzugriffen. Anschließend werden die Wörter der Reihe nach gelesen, bis alle 6 gefunden sind. Bei cat, dem ersten unpassenden Wort, endet die Suche: 12 gelesene Wörter. Eine Hashtabelle weist car, card und cargo Plätze zu, die nichts miteinander zu tun haben. Deshalb muss sie alle 16 Wörter lesen und einzeln prüfen. Beim Einfügen hat der Trie einen Vorteil gegenüber der sortierten Liste. Klick auf carpet einfügen: Der Trie legt 3 Knoten an, ohne etwas zu verschieben. Die sortierte Liste muss dagegen die Zeiger auf 11 Wörter um einen Platz verschieben, um Raum zu schaffen.

40 von 70 Buchstaben gemeinsam genutzt, trotzdem braucht der Trie das 2,6-Fache des Listenspeichers 🖖

Die 16 Wörter haben zusammen 70 Buchstaben. Der Trie speichert sie in 30 Knoten, weil car, card, care und die übrigen Wörter ihre gemeinsamen Buchstaben c, a, r nur einmal ablegen. Ein Knoten braucht allerdings mehr Speicher als ein Buchstabe. In der sparsamen Variante enthält jeder Knoten ein Byte für den Buchstaben, ein Byte für die Wortendmarkierung und 2 Zeiger zu je 8 Byte: einen auf das erste Kind, einen auf den nächsten Geschwisterknoten. Das sind 18 Byte pro Knoten. Einschließlich Wurzel ergeben 31 Knoten insgesamt 558 Byte. Die einfache Liste speichert jeden Buchstaben in einem Byte, dazu pro Wort ein Abschlussbyte und einen Zeiger von 8 Byte: 214 Byte. Bekommt jeder Knoten für jeden Buchstaben des Alphabets einen eigenen Zeiger, genügt pro Schritt ein Arrayzugriff. Dafür braucht der Trie 6.479 Byte, das 30-Fache der Liste. Bei Wörtern mit höchstens 14 Buchstaben wie hier kostet ein neues Wort im Trie nur dann weniger Speicher, wenn es keinen neuen Knoten benötigt, etwa plan in einem Trie, der bereits planet enthält, oder wenn es genau einen Knoten hinzufügt und mindestens 10 Buchstaben hat. Die sparsame Variante kostet auch Zeit: Um einen Buchstaben zu finden, durchsucht sie die Geschwisterknoten der Reihe nach. Für doorbell braucht sie dadurch 10 Vergleiche, einen mehr als die binäre Suche.

Aufgabe vollständig gelöst

  1. Wie viele gemeinsame Präfixe braucht der Trie, um weniger Speicher zu belegen? 4 Schritte

    Klick auf Lange Wörter: 85 Buchstaben in 21 Knoten, drei Viertel davon gemeinsam genutzt. Trotzdem braucht der Trie mit 18-Byte-Knoten 396 Byte, die Liste nur 157. Wenn alle Wörter einer Liste k Buchstaben lang sind, ist der Trie gegenüber der Liste am günstigsten, wenn sie jedes mögliche Wort enthält. Für welche kleinste Wortlänge k braucht dieser Trie bei den hier verwendeten Bytezahlen weniger Speicher als die einfache Liste?

    Öffne diese Aufgabe im Tool

    1. Hier sind es 21 Knoten und die Wurzel: 22 × 18 = 396 Byte. Die Liste speichert 85 Buchstaben und für jedes ihrer 8 Wörter ein Abschlussbyte sowie einen Zeiger von 8 Byte: 85 + 9 × 8 = 157.

    2. Alle Zeichenfolgen der Länge k ergeben 26k Wörter mit je k Buchstaben. Die Liste braucht daher (k + 9) × 26k Byte.

    3. Der Trie braucht für jede Zeichenfolge mit 1 bis k Buchstaben einen Knoten: 26 + 26² + … + 26k, etwas weniger als 26/25 × 26k. Mit der Wurzel und 18 Byte pro Knoten ergibt das knapp 18,72 × 26k Byte.

    4. Die Liste braucht also mehr Speicher, sobald k + 9 größer als 18,72 ist. Das gilt erstmals für k = 10. Bei k = 9 braucht der Trie 4,0 % mehr Speicher als die Liste, bei k = 10 braucht er 1,5 % weniger.

    Antwort

    10 Buchstaben, wenn die Liste alle 2610 möglichen Wörter enthält, also etwa 1,4 × 1014 Wörter. Bei 209-Byte-Knoten muss k + 9 nach derselben Rechnung größer als etwa 217 sein. Dafür braucht es Wörter mit 209 Buchstaben.

Lernpfad

Was eine Datenstruktur mit wenig Aufwand leistet

Quellen (3)