Algorithmen und Datenstrukturen Übung 8: Splay-Bäume Zugrunde liegende Idee Nachdem auf einen Baumknoten zugegriffen wurde, wird dieser Knoten über eine Reihe von AVLRotationen zur Wurzel. Bis zu einem gewissen Grade führt das zur Ausbalancierung. Bsp.: 1. x y x y C A A B B 2. e C e d e d F c d F c b F a a E E E c a A b b A D B A B C C B D C e D a a e c F c d d F A b A b E B C B D C 1 E D Algorithmen und Datenstrukturen Splaying-Operationen Der Knoten „x“ im Splay-Baum bewegt sich über einfache und doppelte Rotationen zur Wurzel. Man unterscheidet folgende Fälle 1. (zig): x ist ein Kind der Wurzel von einem Splay-Baum, einfache Rotation 2. (zig-zig): x hat den Großvater g(x) und den Vater p(x), x und p(x) sind jeweils linke (bzw. rechte) Kinder ihres Vaters. g(x) g(x) = p(y) bzw. p(x) y = p(x) A D x x B C C A D B x y z A B C D 3. (zig-zag): x hat Großvater g(x) und Vater p(x), x ist linkes (rechtes) Kind von p(x), p(x) ist rechtes (linkes) Kind von g(x) z = g(x) x y = p(x) D y z x A A B C 2 B C D Algorithmen und Datenstrukturen Implementierung1 BinaerBaumKnoten // Elementarer Knoten eines binaeren Baums, der nicht ausgeglichen ist // Der Zugriff auf diese Klasse ist nur innerhalb eines Verzeichnisses // bzw. Pakets moeglich class BinaerBaumknoten { // Instanzvariable protected BinaerBaumknoten links; // linker Teilbaum protected BinaerBaumknoten rechts; // rechter Teilbaum public Comparable daten; // Dateninhalt der Knoten // Konstruktor public BinaerBaumknoten(Comparable datenElement) { this(datenElement, null, null ); } public BinaerBaumknoten(Comparable datenElement, BinaerBaumknoten l, BinaerBaumknoten r) { daten = datenElement; links = l; rechts = r; } public void insert (Comparable x) { if (x.compareTo(daten) > 0) // dann rechts { if (rechts == null) rechts = new BinaerBaumknoten(x); else rechts.insert(x); } else // sonst links { if (links == null) links = new BinaerBaumknoten(x); else links.insert(x); } } public BinaerBaumknoten getLinks() { return links; } public BinaerBaumknoten getRechts() { return rechts; } } SplayBaum // // // // // // // // 1 SplayBaum class KONSTRUCTION: ohne Initialisierer ***************** PUBLIC OPERATIONen ******************** void insert( x ) --> Insert x void remove( x ) --> Remove x Comparable find( x ) --> Return item that matches x pr43215 3 Algorithmen und Datenstrukturen // // // // // Comparable findMin( ) Comparable findMax( ) boolean isEmpty( ) void makeEmpty( ) void printTree( ) --> --> --> --> --> Return smallest item Return largest item Return true if empty; else false Remove all items Print tree in sorted order /* * Implementiere einen top-down Splay Baum. * Vergleiche beziehen sich auf die Methode compareTo. */ public class SplayBaum { private BinaerBaumknoten root; private static BinaerBaumknoten nullNode; static // Static initializer for nullNode { nullNode = new BinaerBaumknoten( null ); nullNode.links = nullNode.rechts = nullNode; } private static BinaerBaumknoten newNode = null; // wird in diversen Einfuegevorgaengen benutzt private static BinaerBaumknoten header = new BinaerBaumknoten(null); /* * Konstruktor. */ public SplayBaum( ) { root = nullNode; } /* * Zugriff auf die Wurzel */ public BinaerBaumknoten holeWurzel() { return root; } /* * Insert. * Parameter x ist das einzufuegende Element. */ public void insert( Comparable x ) { if( newNode == null ) newNode = new BinaerBaumknoten( null ); newNode.daten = x; if( root == nullNode ) { newNode.links = newNode.rechts = nullNode; root = newNode; } else { root = splay( x, root ); if( x.compareTo( root.daten ) < 0 ) { newNode.links = root.links; newNode.rechts = root; root.links = nullNode; root = newNode; } else if( x.compareTo( root.daten ) > 0 ) { newNode.rechts = root.rechts; 4 Algorithmen und Datenstrukturen newNode.links = root; root.rechts = nullNode; root = newNode; } else return; } newNode = null; // So next insert will call new } /* * Remove. * Parameter x ist das zu entfernende Element. */ public void remove( Comparable x ) { BinaerBaumknoten neuerBaum; // Falls x gefunden wird, liegt x in der Wurzel root = splay( x, root ); if( root.daten.compareTo( x ) != 0 ) return; // Element nicht gefunden; tue nichts if( root.links == nullNode ) neuerBaum = root.rechts; else { // Finde das Maximum im linken Teilbaum // Splay it to the root; and then attach right child neuerBaum = root.links; neuerBaum = splay( x, neuerBaum ); neuerBaum.rechts = root.rechts; } root = neuerBaum; } /* * Bestimme das kleinste Daten-Element im Baum. * Rueckgabe: kleinstes Datenelement bzw. null, falls leer. */ public Comparable findMin( ) { if( isEmpty( ) ) return null; BinaerBaumknoten ptr = root; while( ptr.links != nullNode ) ptr = ptr.links; root = splay( ptr.daten, root ); return ptr.daten; } /* * Bestiime das groesste Datenelement im Baum. * Rueckgabe: das groesste Datenelement bzw. null, falls leer */ public Comparable findMax( ) { if (isEmpty( )) return null; BinaerBaumknoten ptr = root; while( ptr.rechts != nullNode ) ptr = ptr.rechts; root = splay( ptr.daten, root ); return ptr.daten; } /* * Bestimme ein datenelement im Baum. * Parameter x entfält das zu suchende Element. * Rueckgabe: Das passende Datenelement oder null, falls leer */ public Comparable find( Comparable x ) { root = splay( x, root ); 5 Algorithmen und Datenstrukturen if (root.daten.compareTo( x ) != 0) return null; return root.daten; } /* * Mache den Baum logisch leer. */ public void makeEmpty( ) { root = nullNode; } /* * Ueberpruefe, ob der Baum logisch leer ist if the tree is logically empty. * Rueckgabe true, falls leer, anderenfalls false. */ public boolean isEmpty( ) { return root == nullNode; } /* * Gib den Inhalt des baums in sortierter Folge aus. */ public void printTree( ) { if (isEmpty( )) System.out.println( "Empty tree" ); else printTree( root ); } /* * Ausgabe des Binaerbaums um 90 Grad versetzt */ public void ausgBinaerbaum(BinaerBaumknoten b, int stufe) { if (b != b.links) { ausgBinaerbaum(b.links,stufe + 3); for (int i = 0; i < stufe; i++) { System.out.print(' '); } System.out.println(b.daten.toString()); ausgBinaerbaum(b.rechts,stufe + 3); } } /* * Interne Methode zur Ausfuehrung eines "top down" splay. * Der zuletzt im Zugriff befindliche Knoten * wird die neue Wurzel. * Parameter x Ist das Zielelement, die Umgebung fuer das Splaying. * Parameter b ist die Wurzel des Teilbaums, * um den das Splaying stattfindet. * Rueckgabe des Teilbaums. */ private BinaerBaumknoten splay( Comparable x, BinaerBaumknoten t ) { BinaerBaumknoten leftTreeMax, rightTreeMin; header.links = header.rechts = nullNode; leftTreeMax = rightTreeMin = header; nullNode.daten = x; // Guarantee a match for( ; ; ) if( x.compareTo( t.daten ) < 0 ) { 6 Algorithmen und Datenstrukturen if( x.compareTo( t.links.daten ) < 0 ) t = rotateWithLeftChild( t ); if( t.links == nullNode ) break; // Kette Rechts rightTreeMin.links = t; rightTreeMin = t; t = t.links; } else if( x.compareTo( t.daten ) > 0 ) { if( x.compareTo( t.rechts.daten ) > 0 ) t = rotateWithRightChild( t ); if( t.rechts == nullNode ) break; // Kette Links leftTreeMax.rechts = t; leftTreeMax = t; t = t.rechts; } else break; leftTreeMax.rechts = t.links; rightTreeMin.links = t.rechts; t.links = header.rechts; t.rechts = header.links; return t; } /* * Rotation BinaerBaumknoten mit linkem Nachfolger. */ static BinaerBaumknoten rotateWithLeftChild(BinaerBaumknoten k2) { BinaerBaumknoten k1 = k2.links; k2.links = k1.rechts; k1.rechts = k2; return k1; } /* * Rotation BinaerBaumknoten mit rechtem Nachfolger. */ static BinaerBaumknoten rotateWithRightChild(BinaerBaumknoten k1) { BinaerBaumknoten k2 = k1.rechts; k1.rechts = k2.links; k2.links = k1; return k2; } /* * Interne Methode zur Ausgabe eines Teilbaums in sortierter Folge. * Parameter b ist der jweilige Wurzelknoten. */ private void printTree( BinaerBaumknoten b ) { if( b != b.links ) { printTree(b.links); System.out.println(b.daten.toString( )); printTree(b.rechts); } } } SplaybaumTest import java.io.*; 7 Algorithmen und Datenstrukturen public class SplayBaumTest { public static void main( String [ ] args ) { SplayBaum b = new SplayBaum(); String eingabeZeile = null; System.out.println("Einfuegen"); BufferedReader eingabe = null; eingabe = new BufferedReader( new InputStreamReader(System.in)); try { int zahl; do { System.out.println("Zahl? "); eingabeZeile = eingabe.readLine(); try { zahl = Integer.parseInt(eingabeZeile); b.insert(new Integer(zahl)); b.ausgBinaerbaum(b.holeWurzel(),2); } catch (NumberFormatException ne) { break; } } while (eingabeZeile != ""); } catch (IOException ioe) { System.out.println("Eingefangen in main()"); } System.out.println("Loeschen"); try { int zahl; do { System.out.println("Zahl? "); eingabeZeile = eingabe.readLine(); try { zahl = Integer.parseInt(eingabeZeile); b.remove(new Integer(zahl)); b.ausgBinaerbaum(b.holeWurzel(),2); } catch (NumberFormatException ne) { break; } } while (eingabeZeile != ""); } catch (IOException ioe) { System.out.println("Eingefangen in main()"); } System.out.println("Zugriff auf das kleinste Element"); b.findMin(); b.ausgBinaerbaum(b.holeWurzel(),2); System.out.println("Zugriff auf das groesste Element"); b.findMax(); b.ausgBinaerbaum(b.holeWurzel(),2); } } 8