homedukeAlgorithmen & Datenstrukturen mit Java: Patricia-Bäume Prof. Dr. Uwe Schmidt FH Wedel

Patricia-Bäume

weiter

weiter

Patricia-Baum
Radix Tree, (Compact) Prefix Tree
weiter
Ziel
Effiziente Implementierung für Mengen und Verzeichnisse
mit Zeichenketten über einem Alphabet A als Schlüssel.
weiter
O(1)
Die Laufzeit für das Suchen und Einfügen ist ∈ O(1) bezüglich der Anzahl der Elemente in einer Menge/Map
 
Die Laufzeit hängt nur noch von der Länge (Anzahl Zeichen im Schlüssel) ab.
Präfix-Suche
Diese Datenstruktur ist sehr gut geeignet für eine Präfixsuche
 
Berechnung aller Schlüssel(-Wert-Paare) für ein Wort
Alphabet
endliche, nicht leere Menge von Zeichen
 
Unicode, ASCII, {0,1}, ...
weiter
Datenstruktur
Baumstruktur (also wieder rekursiv)
Knoten
es gibt innere Knoten und Blätter
Blatt
besteht aus dem Schlüssel k und dem zugeordneten Wert v
 
Optimierung: Der Schlüssel k muss nicht zwingend explizit im Blatt gespeichert werden
innere Knoten
bestehen aus einer Tabelle von Nachfolgern
 
ein Nachfolger besteht aus einem Präfix p über dem Alphabet und einem Teilbaum tp
 
außerdem kann optional noch ein Schlüssel-Wert-Paar (k,v) in dem inneren Knoten enthalten sein
leerer Baum
Der leere Baum wird durch einen Spezialwert (Empty) dargestellt.
Nur die Wurzel eines Baumes kann diesen Wert enthalten.
weiter
Invariante
.1
Alle inneren Knoten besitzen mindestens einen Nachfolger
.2
Alle inneren Knoten, an denen kein Wert gespeichert ist, besitzen mindestens zwei Nachfolger
Diese Einschränkung gilt nicht für die Wurzel.
.3
Alle Nachfolger-Wörter (Präfixe) bestehen mindestens aus einem Zeichen
.4
Es gibt keine zwei Nachfolger-Wörter, die mit dem gleichen Zeichen beginnen
weiter
Klassenstruktur (Skizze)
public abstract
    class PatriciaTree
    implements Map {
 
    ...
 
    static class Empty
        extends PatriciaTree {
        ...
    }
 
    static class Leaf
        extends PatriciaTree {
        String k;
        Value  v;
        ...
    }
 
    static class Node
        extends PatriciaTree {
        SubTreeMap st;
        Leaf       mark;  // null: not there
        ...
    }
 
    static class SubTreeMap {
        java.util.Map<String,PatriciaTree> trees;
        ...
    }
    // or something more specialized, e.g.
 
    static class SubTreeMap {
         String       [] prefixes;
         PatriciaTree [] subtrees;  // same length as prefixes
         ...
    }
}
weiter
Suchen
eines Schlüssels k in einem Baum t
.1
wenn k = "" und t ein Blatt mit einem v oder ein innerer Knoten mit Zusatzinformation v ist, dann ist die Suche erfolgreich und v ist das Resultat
.2
wenn k = "x..." und t ein innerer Knoten ist und es unter den Nachfolgern ein p gibt, der ein echter Präfix von k ist, dann wird mit dem p zugeordneten Teilbaum tp und dem um p verkürztem Schlüssel kp die Suche fortgesetzt.
.3
in allen anderen Fällen ist die Suche gescheitert
merke
In .2 gibt es wegen Invariante .4 höchstens einen Nachfolger-Baum mit der Präfix-Eigenschaft
weiter
Einfügen
eines Schlüssel(-Wert-Paare)s (k,v) in einen Baum t
 
Der Baum wird wie bei der Suche auf einem Pfad besucht
.0
wird der Schlüssel k im Baum t gefunden, so wird der Wert im Baum an dieser Stelle mit v überschrieben
.1
sei t1 der innerer Knoten, an dem die Suche scheitert und ki der während der Suche aus k schrittweise verkürzte Schlüssel
.a
ist ki = "", so wird das Paar (k, v) im Knoten t1 als Zusatzinformation gespeichert
.b
sei ki = "x..." und x kommt nicht als 1. Zeichen in den Nachfolger-Wörtern von t1 vor, so wird (ki = "", l) in die Nachfolger-Menge von t1 aufgenommem, wobei l ein Blatt bestehend aus (k,v) ist
.c
sei kj mit kj = "x..." das Nachfolger-Wort mit dem gleichen Anfangszeichen, so wird der längste gemeinsame Präfix pij von ki und kj berechnet und ein neuer innerer Knoten tij mit pij als Nachfolger-Wort eingefügt. Der neue Baum tij hat als Teilbäume den alten Nachfolger-Baum und ein neues Blatt für (k,v).
Als Präfixe für diese Bäume werden die Reste bezüglich pij genommen.
weiter
Laufzeitanalyse
Suchen
.1
läuft in konstanter Zeit
.2
Sei n die Anzahl der Zeichen im Alphabet.
 
Da ein innerer Knoten höchstens n Nachfolger besitzt und für die Suche nach dem Nachfolger mit gemeinsamem Präfix nur das 1. Zeichen verglichen werden muss, ist die Suche in der Zeit beschränkt.
 
Für die Berechnung des längsten Präfixes müssen die Folgezeichen nur einmal verglichen werden, also pro Zeichen wieder eine beschränkte Zeit.
Rekursion
Der Prozess des Suchens wird maximal m mal wiederholt mit m = Länge von k.
gut
Die Suchzeit ist bezüglich der Länge der Schlüssel in O(m).
gut
Die Suchzeit hängt nicht von der Anzahl der Einträge in der Menge/Map ab.
Grund dafür ist die endliche Anzahl von Zeichen in A.
weiter
Einfügen
Die Argumentation für die Suche kann 1-1 übertragen werden.
.1a und .1b
Die Arbeit für das Einfügen an dem Knoten, an dem die Suche scheitert, enthält keinerlei Wiederholungen, sondern besteht aus einer Sequenz von elementaren Operationen.
gut
Beim Einfügen wird höchstens ein neuer Knoten erzeugt.
gut
Gleiche Laufzeitabschätzungen wie bei der Suche.
weiter

weiter

1. Spezialfall

Alphabet
{0,1}
Konsequenzen
.1
maximal 2 Nachfolger, binäre Suche
gut
Struktur der inneren Knoten wird stark vereinfacht
gut
Platzbedarf für die inneren Knoten wird verringert
.2
Innere Knoten mit nur einem Nachfolger besitzen immer einen zusätzlichen (k,v)-Wert
Konsequenzen
Werden die an den inneren Knoten gespeicherten Werte in extra Objekte ausgelagert, kann die Struktur weiter vereinfacht werden, da dann alle Verzeigungen immer binär sind.
gut
Struktur der inneren Knoten wird stark vereinfacht
gut
Platzbedarf für die inneren Knoten wird verringert
?
Praxisrelevant
weiter
Klassenstruktur (Skizze)
public abstract
    class BinPatriciaTree
    implements Map {
 
    // boring case for empty tree
    static class Empty
        extends BinPatriciaTree {
        ...
    }
 
    static class Leaf
        extends BinPatriciaTree {
        String k;
        Value  v;
        ...
    }
 
    static class Mark
        extends BinPatriciaTree {
        Leaf            mark;
        BitString       prefix;
        BinPatriciaTree subTree;
        ...
    }
 
    static class Fork
        extends BinPatriciaTree {
 
        BinPatriciaTree l;
        BinPatriciaTree r
 
        BitString prefix;
        ...
    }
 
    static class BitString {
        boolean [] theBits;
        ...
    }
}
weiter

weiter

2. Spezialfall

Einschränkungen
zusätzlich zu 1.
Alle Schlüssel besitzen die gleiche Länge, z.B. 32 oder 64
Vereinfachungen
gut
innere Knoten besitzen keine zusätzlichen (k,v)-Markierungen
enthalten also nur noch binäre Verzweigungen
gut
Schlüssel sind Binärzahlen, also Maschinenwörter
gut
== (equals) ist in einer Maschineninstruktion zu machen
gut
Präfix-Operationen können mit Bitoperationen realisiert werden.
Sie benötigen nur noch eine kurze Sequenz von Maschineninstrukionen, also auch eine konstante Zeit
?
Praxisrelevant
Klassenstruktur (Skizze) für 32 bit Schlüssellänge
public abstract
    class IntMap
    implements Map {
 
    // boring case for empty tree
    static class Empty
        extends IntMap {
        ...
    }
 
    static class Leaf
        extends IntMap {
        int   k;
        Value v;
        ...
    }
 
    static class Fork
        extends IntMap {
 
        IntMap l;
        IntMap r
 
        int prefix;
        int mask;
        ...
    }
}

Letzte Änderung: 23.01.2018
© Prof. Dr. Uwe Schmidt
Prof. Dr. Uwe Schmidt FH Wedel