Algorithmen und Datenstrukturen Übung 7: AA-Bäume Die Implementierung eines Rot-Schwarz-Baums ist sehr trickreich (insbesondere das Löschen von Baumknoten). Bäume, die diese trickreiche Programmierung einschränken, sind der binäre B-Baum und der AA-Baum. Definitionen Ein BB-Baum ist ein Rot-Schwarz-Baum mit einer besonderen Bedingung: Ein Knoten hat, falls es sich ergibt, eine „roten“ Nachfolger“. Zur Vereinfachung der Implementierung kann man noch einige Regeln hinzufügen und darüber einen AA-Baum definieren: 1. Nur ein Nachfolgeknoten kann rot sein. Das bedeutet: Falls ein interner Knoten nur einen Nachfolger hat, dann muß er rot sein. Ein schwarzer Nachfolgeknoten verletzt die 4. Bedingung von Rot-Schwarz-Bäumen. Damit kann ein innerer Knoten immer durch den kleinsten Knoten seines rechten Teilbaums ersetzt werden. 2. Anstatt ein Bit zur Kennzeichnung der Färbung wird die Stufe zum jeweiligen Knoten bestimmt und gespeichert. Die Stufe eines Knoten ist - Eins, falls der Knoten ein Blatt ist. - Die Stufe eines Vorgängerknoten, falls der Knoten rot gefärbt ist. - Die Stufe ist um 1 niedriger als die seines Vorgängers, falls der Knoten schwarz ist. Eine horizontale Verkettung zeigt die Verbindung eines Knoten und eines Nachfolgeknotens mit gleicher Stufenzahl an. Der Aufbau verlangt, daß horizontale Verkettungen rechts liegen und daß es keine zwei aufeinander folgende horizontale Verkettungen gibt. Darstellung In einen zunächst leeren AA-Baum sollen Schlüssel in folgender Reihenfolge eingefügt werden: 10, 85 15, 70, 20, 60, 30, 50, 65, 80, 90, 40, 5, 55, 35. 10 10 85 10 85 15 10 15 85 „skew“ 15 „slip“ 10 85 70 15 10 70 1 85 Algorithmen und Datenstrukturen 20 15 10 70 20 85 60 15 10 70 20 60 85 30 15 10 70 20 60 85 30 15 10 30 20 70 60 15 85 30 20 70 60 85 30 15 10 70 20 2 60 85 Algorithmen und Datenstrukturen 50 30 15 70 10 20 50 60 85 65 30 15 60 10 20 70 50 65 85 80 30 15 10 60 20 70 50 65 80 90 30 70 15 10 60 20 50 85 65 80 90 40 30 70 15 10 60 20 40 3 50 85 65 80 90 85 Algorithmen und Datenstrukturen 5 30 70 15 5 60 10 20 40 85 50 65 80 90 55 30 70 15 5 50 10 20 40 60 55 85 65 80 90 35 30 15 5 10 70 20 50 35 40 60 55 85 65 Implementierung Der Knoten des AA-Baums: // Baumknoten fuer AA-Baeume class AAKnoten { public Comparable daten; // Datenelement im Knoten protected AAKnoten links; // linkes Kind protected AAKnoten rechts; // rechtes Kind protected int level; // Level // Konstruktoren AAKnoten(Comparable datenElement) { this(datenElement, null, null); } AAKnoten(Comparable datenElement, AAKnoten l, AAKnoten r) { daten = datenElement; links = l; rechts = r; level = 1; } 4 80 90 Algorithmen und Datenstrukturen } Operationen an AA-Bäumen: Ein „Sentinel“ repräsentiert „null“. Suchen. Es erfolgt nach die in binären Suchbäumen üblichen Weise. Einfügen von Knoten. Es erfolgt auf der untersten Stufe (bottom level) /* * Interne Methode zum Einfuegen in einen Teilbaum. * "x" enthaelt das einzufuegende Element. * "b" ist die wurzel des baums. * Rueckgabe: die neue Wurzel. */ private AAKnoten insert(Comparable x, AAKnoten b) { if (b == nullNode) b = new AAKnoten(x, nullNode, nullNode); else if (x.compareTo(b.daten) < 0 ) b.links = insert(x, b.links); else if (x.compareTo(b.daten) > 0 ) b.rechts = insert(x, b.rechts); else return b; b = skew(b); b = split(b); return b; } /* * Skew Primitive fuer AA-Baeume. * "b" ist die Wurzel. * Rueckgabe: die neue wurzel nach der Rotation. */ private AAKnoten skew(AAKnoten b) { if (b.links.level == b.level ) b = rotationMitLinksNachf(b); return b; } /* * Split fuer AABaeume. * "b" ist die Wurzel. * Rueckgabe: die neue wurzel nach der Rotation. */ private AAKnoten split(AAKnoten b) { if (b.rechts.rechts.level == b.level ) { b = rotationMitRechtsNachf(b); b.level++; } return b; } 5 Algorithmen und Datenstrukturen Bsp.: 30 70 15 5 50 10 20 35 60 40 55 85 65 80 90 1. Einfügen des Schlüssels 2 Erzeugt wird ein linke horizontale Verknüpfung 2. Einfügen des Schlüssels 45 Erzeugt werden zwei rechte horizontale Verbindungen 30 70 15 2 50 5 10 20 35 40 60 45 55 85 65 80 90 In beiden Fällen können einfache Rotationen ausgleichen: - Linke horizontale Verknüpfungen werden durch ein einfache rechte Rotation behoben („skew“) X P X P C A C B A B Abb.: Rechtsrotation z.B.: 30 5 2 70 15 10 50 20 35 40 6 45 60 55 85 65 80 90 Algorithmen und Datenstrukturen - Aufeinanderfolgende rechte horizontale Verknüpfungen werden durch einfach linke Rotation behoben (split) X R G R C X A G B C A B Abb.: Linksrotation z.B. „Einfügen des Schlüssels 45“ 30 70 15 5 10 50 20 35 40 45 60 55 85 65 80 90 „split“ am Knoten mit dem Schlüssel 35 30 15 5 10 70 40 20 35 50 45 60 55 85 65 80 90 „skew“ am Knoten mit dem Schlüssel 50 30 15 5 10 70 40 20 50 35 45 7 60 55 85 65 80 90 Algorithmen und Datenstrukturen „split“ am Knoten mit dem Schlüssel 40 30 70 15 5 50 10 20 85 40 35 60 45 55 80 90 65 Endgültige Baumgestalt nach „skew“ am Knoten 70 und „split“ am Knoten 30 50 30 70 15 5 10 40 20 35 60 45 55 8 85 65 80 90 Algorithmen und Datenstrukturen Löschen von Knoten. /* * Interne Methode fuer das Entfernen aus einem Teilbaum. * Parameter x ist das zu entfernende Merkmal. * Parameter b ist die Wurzel des Baums. * Rueckgabe: die neue Wurzel. */ private AAKnoten remove(Comparable x, AAKnoten b) { if( b != nullNode ) { // Schritt 1: Suche bis zum Grund des Baums, setze lastNode und // deletedNode lastNode = b; if( x.compareTo( b.daten ) < 0 ) b.links = remove( x, b.links ); else { deletedNode = b; b.rechts = remove( x, b.rechts ); } // Schritt 2: Falls am Grund des Baums und // x ist gegenwaertig, wird es entfernt if( b == lastNode ) { if( deletedNode == nullNode || x.compareTo( deletedNode.daten ) != 0 ) return b; // Merkmal nicht gefunden; es geschieht nichts deletedNode.daten = b.daten; b = b.rechts; } // Schritt 3: Anderenfalls, Boden wurde nicht erreicht; Rebalancieren else if( b.links.level < b.level - 1 || b.rechts.level < b.level - 1 ) { if( b.rechts.level > --b.level ) b.rechts.level = b.level; b = skew( b ); b.rechts = skew( b.rechts ); b.rechts.rechts = skew( b.rechts.rechts ); b = split( b ); b.rechts = split( b.rechts ); } } return b; } 9