Algorithmen und Datenstrukturen Fachhochschule Regensburg Algorithmen und Datenstrukturen AVL-Bäume 1 9. Übungsblatt Algorithmen und Datenstrukturen Name: ________________________ Lehrbeauftragter: Prof. Sauer Vorname: _____________________ Abb. 1: Darstellung eines AVL-Baums mit Balancierungsangeben (Höhendifferenzen) 2 1. Einfügen von Knoten im AVL-Baum 3 Ein binärer Suchbaum hat die AVL-Eigenschaft, wenn sich in jedem Knoten sich die Höhen der beiden Teilbäume höchstens um 1 unterscheiden. Diese Last („Balance“) muß in einem Knoten gespeichert sein. Es genügt aber als Maß für die Unsymmetrie eine Höhendifferenz festzuhalten, die nur die Werte –1 (linkslastig), 0 (gleichlastig) und +1 (rechtslastig) annehmen kann. Die AVL-Eigenschaft ist verletzt, wenn diese Höhendifferenz +2 bzw. –2 ist. Der Knoten, der diesen Wert erhalten hat, ist der Knoten „alpha“, dessen Unausgeglichenheit auf einen der folgenden 4 Fälle zurückzuführen ist: 1. Einfügen in den linken Teilbaum, der vom linken Nachkommen des Knoten „alpha“ bestimmt ist. 2. Einfügen in den rechten Teilbaum, der vom linken Nachkommen des Knoten „alpha“ bestimmt ist. 3. Einfügen in den linken Teilbaum, der vom rechten Nachkommen des Knoten „alpha“ bestimmt ist. 4. Einfügen in den rechten Teilbaum, der vom rechten Nachkommen des Knoten „alpha“ bestimmt ist Fall 1 und Fall 4 bzw. Fall 2 und Fall 3 sind Spiegelbilder, zeigen also das gleiche Verhalten. Fall1 kann durch einfache Rotation behandelt werden und ist leicht zu bestimmen, daß das Einfügen „außerhalb“ (links – links bzw. rechts – rechts im Fall 4) stattfindet. Fall 2 kann durch doppelte Rotation behandelt werden und ist ebenfalls leicht zu bestimmen, da das Einfügen „innerhalb“ (links –rechts bzw. rechts – links) erfolgt. Die einfache Rotation Die folgende Darstellung beschreibt den Fall 1 vor und nach der Rotation: 1 Vgl. Skriptum 4.3.2 vgl. AVLTreeTest.java, AVLTree.java 3 vgl. Skriptum, 4.3.2 2 1 Algorithmen und Datenstrukturen k2 k1 k1 k2 Z Y X Y Z X Die folgende Darstellung beschreibt Fall 4 vor und nach der Rotation : k1 k2 k2 k1 X Y X Y Z Z Doppelrotation Die einfache Rotation führt in den Fällen 2 und 3 nicht zum Erfolg. Fall 2 muß durch eine Doppelrotation (links – rechts) behandelt werden. k3 k2 k1 k1 k3 D k2 B A A B C 2 C D Algorithmen und Datenstrukturen Auch Fall 3 muß durch Doppelrotation behandelt werden k1 k3 k2 k1 k2 A k3 D B A B C D C Implementierung 4 Zum Einfügen eines Knoten mit dem Datenwert „x“ in einen AVL-Baum, wird „x“ rekursiv in den betoffenen Teilbaum eingesetzt. Falls die Höhe dieses Teilbaums sich nicht verändert, ist das Einfügen beendet. Liegt Unausgeglichenheit vor, dann ist einfache oder doppelte Rotation (abhängig von „x“ und den Daten des betroffenen Teilbaums) nötig. AVL-Baumknoten Er enthält für jeden Knoten eine Angabe zur Höhe(ndifferenz) 5 seiner Teilbäume. // Baumknoten fuer AVL-Baeume class AvlKnoten { // Instanzvariable protected AvlKnoten links; // Linkes Kind protected AvlKnoten rechts; // Rechtes Kind protected int hoehe; // Hoehe public Comparable daten; // Das Datenelement // Konstruktoren public AvlKnoten(Comparable datenElement) { this(datenElement, null, null ); } public AvlKnoten( Comparable datenElement, AvlKnoten lb, AvlKnoten rb ) { daten = datenElement; links = lb; rechts = rb; hoehe = 0; } } Der AVL-Baum Bei jedem Schritt ist festzustellen, ob die Höhe des Teilnaums, in dem ein Element eingefügt wurde, zugenommen hat. /* * Rueckgabe: Hoehe des Knotens, oder -1, falls null. */ 4 5 im Verzeichnis ueb09 steht die aktuellen Version, /ueb/pr43210/GenAVLBaum.java vgl. Skriptum, 4.3.3 3 Algorithmen und Datenstrukturen private static int hoehe(AvlKnoten b) { return b == null ? -1 : b.hoehe; } Die Methode „insert“ führt das Einfügen eines Baumknoten in den Avl-Baum aus: /* * Interne Methode zum Einfuegen eines Baumknoten in einen Teilbaum. * x ist das einzufuegende Datenelement. * b ist der jeweilige Wurzelknoten. * Rueckgabe der neuen Wurzel des jeweiligen Teilbaums. */ private AvlKnoten insert(Comparable x, AvlKnoten b) { if( b == null ) b = new AvlKnoten(x, null, null); else if (x.compareTo( b.daten) < 0 ) { b.links = insert(x, b.links ); if (hoehe( b.links ) - hoehe( b.rechts ) == 2 ) if (x.compareTo( b.links.daten ) < 0 ) b = rotationMitLinksNachf(b); else b = doppelrotationMitLinksNachf(b); } else if (x.compareTo( b.daten ) > 0 ) { b.rechts = insert(x, b.rechts); if( hoehe(b.rechts) - hoehe(b.links) == 2) if( x.compareTo(b.rechts.daten) > 0 ) b = rotationMitRechtsNachf(b); else b = doppelrotationMitRechtsNachf( b ); } else ; // Duplikat; tue nichts b.hoehe = max( hoehe( b.links ), hoehe( b.rechts ) ) + 1; return b; } Einfache Rotation RotationMitLinksNachf dreht den Linken Teilbaum der folgenden Darstellung nach rechts. k2 k1 k1 k2 Z Y X Y Z X /* * Rotation Binaerbaumknoten mit linkem Nachfolger. * Fuer AVL-Baeume ist dies eine einfache Rotation (Fall 1). 4 Algorithmen und Datenstrukturen * Aktualisiert Angaben zur Hoehe, dann Rueckgabe der neuen Wurzel. */ private static AvlKnoten rotationMitLinksNachf(AvlKnoten k2) { AvlKnoten k1 = k2.links; k2.links = k1.rechts; k1.rechts = k2; k2.hoehe = max( hoehe( k2.links ), hoehe( k2.rechts ) ) + 1; k1.hoehe = max( hoehe( k1.links ), k2.hoehe ) + 1; return k1; } Doppelrotation “doppelrotationMitLinksNachf” führt die folgende Umstellung der Baumknoten aus: k3 k2 k1 k1 k3 D k2 B A C A B D C /* * Doppelrotation Fall 2: erstes linkes Kind mit seinem rechten Kind; * dann Knoten k3 mit neuem linken Kind. * Aktualisieren der Hoehen, * dann Rueckgabe der neuen Wurzel. */ private static AvlKnoten doppelrotationMitLinksNachf(AvlKnoten k3) { k3.links = rotationMitRechtsNachf( k3.links ); return rotationMitLinksNachf( k3 ); } 2. Löschen von Knoten im AVL-Baum Das Löschen eines Knotens (Schlüssel x) mit 2 Kindern kann auf das Löschen von Blatt-/Randknoten zurückgeführt werden. Für innere Knoten gilt: - „x“ wird ersetzt durch den kleinsten Schlüssel y im rechten Teilbaum von x (bzw. größten Schlüssel im linken Teilbaum). Führt zur Änderung des Balancierungsfaktors für v (Höhe des linken Teilbaums von v hat sich um 1 reduziert). 5 Algorithmen und Datenstrukturen x y ⇒ v v Bl Bl z y Bry Br Br Bry z Bis auf Symmetrie treten nur 3 Fälle auf. Fall 1: 0 v h+1 1 h+1 Löschen in Bl ⇒ h h h-1 Bl h Br Bl Br In diesem Fall pflanzt sich Höhenerniedrigung nicht fort, da in der Wurzel das AVL-Kriterium erfüllt bleibt, d.h. kein Rebalancieren erforderlich. Fall 2: -1 h+1 0 h Löschen in Bl ⇒ h h-1 Bl h-1 Br Bl Br h-1 Die Höhenerniedrigung von Bl pflanzt sich hier zur Wurzel fort. Sie kann auf diesem Pfad eine Rebalancierung auslösen. Fall 3: 6 Algorithmen und Datenstrukturen -1 h+1 -2 Löschen in ⇒ h Br h-1 h Br Bl h+1 h-2 Br Bl Für die Behandlung dieser Situation ist der linke Teilbaum Bl genauer zu betrachten. Dabei ergeben sich 3 Unterfälle: Fall 3a: -2 x4 1 x2 h+1 h+1 ⇒ 0 x2 -1 x4 B5 B h-2 h-1 B1 h-1 h-1 B1 h-1 B3 0 x2 h h-2 B5 h-2 B5 B3 - Rechtsrotation führt zur Erfüllung des AVL-Kriteriums - Teilbaum behält ursprüngliche Höhe - Keine weiteren Rebalancierungen erforderlich Fall 3b: -2 x4 h+1 ⇒ -1 x2 0 x4 B5 B h-2 h-1 B1 h-1 h-2 B1 h-2 B3 B3 - Rechtsrotation reduziert Höhe des gesamten Teilbaum von h+1 nach h - Höhenreduzierung pflanzt sich auf dem Pfad zur Wurzel hin fort und kann zu weiteren Rebalancierungen führen. 7 Algorithmen und Datenstrukturen -2 h+1 x6 +1 x2 0 x4 h h h-2 B7 0 x2 B 0 x4 0 x6 h-2 h-2 B1 B3 B5 B7 B1 h-2 h-2 B3 B5 B - Doppelrotation - ggf. fortgesetzte Rebalancierungen Rebalancierungsschritte beim Löschen 1. Suche im Löschpfad nähesten Vater mit Balancierungsfaktor +/-2 2. Führe ggf. Rotation im gegenüberliegenden Teilbaum dieses Vaters auf. Im Gegensatz zum Einfügevorgang kann hier eine Rotation wiederum auf dem Pfad zur Wurzel auslösen, da sie in gewissen Fällen auf eine Höhenerniedrigung des transformierten Teilbaums führt. Die Anzahl der Rebalancierungsschritte ist jedoch durch die Höhe h des Baums begrenzt. Höhe von AVL-Bäumen Balancierte Bäume werden als Kompromiß zwischen ausgeglichenen und natürlichen Suchbäumen eingeführt, wobei „logarithmischer Suchaufwand“ im schlechtesten Fall gefordert wurde. Für die h +1 maximale Anzahl von Knoten n gilt folgende Ungleichung: n ≤ 2 − 1 . Daraus erhält man zur h ≤ 2 ⋅ log 2 n + 1 . Das bedeutet, dass die Höhe eines AVL-Baums von der Wurzel bis zu einem beliebigen Knoten höchstens 2 ⋅ log 2 n + 1 ist Abschätzung der Höhe: Komplexität von Opeartionen aujf AVL-Bäumen - Komplexität für Einfügen und Löschen in binären Suchbäumen: O (h) . -- im AVL- Baum gilt: h = O (log n) -- Notwendige Rotationen sind in konstanter Zeit auszuführen - Ausbalancieren nach Einfügen erfordert höchstens eine (einfache oder doppelte) Rotation. Komplexität von insert: O (log n) + O (1) = O (log n) - Ausbalancieren nach Löschen erfordert höchstens h (einfache und doppelte) Rotationen. Komplexität von remove: O (log n) + O (log n) = O (log n) - Einfügen und Löschen in AVL-Bäumen haben im schlechtesten Fall eine Komplexität von O(logn) - Zusammenfassung: worst-case Komplexität O (log n ) für Suchen, Einfügen und Löschen. 8