ueb4 - oth-regensburg.de

Werbung
Algorithmen und Datenstrukturen
Übung 4: Die generische Klasse „AvlBaum“ in Java1
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.
Fall 1 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:
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
Doppelrotation
Die einfache Rotation führt in den Fällen 2 und 3 nicht zum Erfolg.
1
Vgl. PR43210
1
Z
Algorithmen und Datenstrukturen
Fall 2 muß durch eine Doppelrotation (links – rechts) behandelt werden.
k3
k2
k1
k1
k3
D
k2
B
A
C
A
B
D
C
Auch Fall 3 muß durch Doppelrotation behandelt werden
k1
k2
k2
k1
k3
A
k3
D
B
A
B
C
D
C
Implementierung
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) 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;
2
Algorithmen und Datenstrukturen
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.
*/
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;
}
3
Algorithmen und Datenstrukturen
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).
* 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
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 );
}
4
D
Algorithmen und Datenstrukturen
Lösungen
// 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;
}
}
// Binaerer Suchbaum
//
//
//
//
//
//
//
//
//
//
//
//
//
Konstruktor: Initialisierung der Wurzel mit null
*** oeffentlich zugaengliche Methoden ********************
void insert( x )
--> Fuege x ein
void remove( x )
--> Entferne x
Comparable find( x )
--> Gib das Element zurueck, das zu x passt
Comparable findMin( ) --> Rueckgabe des kleinsten Elements
Comparable findMax( ) --> Rueckgabe des groessten Elements
boolean isEmpty( )
--> Return true if empty; else false
void makeEmpty( )
--> Entferne alles
void printTree( )
--> Ausgabe der Baum-Elemente in sort. Folge
void ausgBinaerBaum() --> Ausgabe der Binaerbaum-Elemente
/*
* Implementierung AVL-Baum
* Vergleiche basieren auf der Methode compareTo.
*/
public class AvlBaum
{
/* Wurzel des Baums */
private AvlKnoten wurzel;
/*
* Default-Konstruktor
*/
public AvlBaum( )
{
wurzel = null;
}
/*
* Einfuegen; Duplikaten werden ignoriert.
* x ist das einzufuegende Datenelement.
5
Algorithmen und Datenstrukturen
*/
public void insert(Comparable x )
{
wurzel = insert( x, wurzel );
}
/*
* Entfernen eines Baumknotens. Falls x nicht gefunden wird,
* geschieht nichts.
* x ist das zu entfernende Datenelement.
*/
public void remove(Comparable x )
{
System.out.println( "Entschuldigung, remove wurde nicht implementiert"
);
}
/*
* Bestimme das kleinste Datenelement im Baum..
* Rueckgabe: kleinstes Datenelement oder null,
* falls der Baum leer ist.
*/
public Comparable findMin( )
{
return elementAt( findMin(wurzel));
}
/*
* Finde das groesste Datenelement im Baum.
* Rueckgabe: groesstes Datenelement oder null, falls der Baum leer ist.
*/
public Comparable findMax( )
{
return elementAt(findMax(wurzel));
}
/*
* Finde ein datenelement im Baum.
* x enthaelt das zu suchende Datenelement.
* Rueckgabe: das gesuchte Datenelement oder null,
* falls es nicht gefunden wurde.
*/
public Comparable find(Comparable x)
{
return elementAt(find(x, wurzel));
}
/*
* Loeschen des Baums.
*/
public void makeEmpty( )
{
wurzel = null;
}
/*
* Test, ob der Baum leer ist.
* Rueckgabe true, falls leer; anderenfalls false.
*/
public boolean isEmpty( )
{
return wurzel == null;
}
/*
* Gib den Inhalt des Baums in sortierter Folge vor.
*/
public void printTree( )
{
if( isEmpty( ) )
6
Algorithmen und Datenstrukturen
System.out.println( "Baum ist leer" );
else
printTree( wurzel );
}
/*
* Ausgabe der Elemente des binaeren Baums um 90 Grad versetzt
*/
public void ausgBinaerBaum()
{
if( isEmpty() )
System.out.println( "Leerer baum" );
else
ausgBinaerBaum(wurzel,0);
}
/*
* Interne Methode fuer den Zugriff auf einen Baumknoten.
* Parameter b referenziert den Baumknoten.
* Rueckgabe des Datenelements im Baumknoten oder null,
* falls b null ist.
*/
private Comparable elementAt(AvlKnoten b )
{
return b == null ? null : b.daten;
}
/*
* 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;
}
/*
* Interne Methode zum Bestimmen des kleinsten Datenelements.
* b ist die Wurzel.
* Rueckgabe: Knoten mit dem kleinsten Datenelement.
*/
private AvlKnoten findMin(AvlKnoten b)
7
Algorithmen und Datenstrukturen
{
if (b == null)
return b;
while(b.links != null )
b = b.links;
return b;
}
/*
* Interne Methode zum Bestimmen des groessten Datenelements.
* b ist die Wurzel.
* Rueckgabe: Knoten mit dem groessten Datenelement.
*/
private AvlKnoten findMax(AvlKnoten b )
{
if (b == null)
return b;
while (b.rechts != null)
b = b.rechts;
return b;
}
/*
* Internal Methode zum Bestimmen eines Datenelements.
* x ist das zu suchende Datenelement.
* b ist die Wurzel.
* Rueckgabe: Knoten mit dem passenden Datenelement.
*/
private AvlKnoten find(Comparable x, AvlKnoten b)
{
while( b != null )
if (x.compareTo( b.daten) < 0 )
b = b.links;
else if( x.compareTo( b.daten ) > 0 )
b = b.rechts;
else
return b;
// Passt
return null;
// Passt nicht
}
/*
* Interne Methode zur Ausgabe eines Teilbaums in sortierte Folge.
* b ist die Wurzel.
*/
private void printTree(AvlKnoten b)
{
if( b != null )
{
printTree( b.links );
System.out.println( b.daten );
printTree( b.rechts );
}
}
/*
* Ausgabe des Binaerbaums um 90 Grad versetzt
*/
private void ausgBinaerBaum(AvlKnoten b, int stufe)
{
if (b != null)
{
ausgBinaerBaum(b.links, stufe + 1);
for (int i = 0; i < stufe; i++)
{
System.out.print(' ');
}
System.out.println(b.daten);
8
Algorithmen und Datenstrukturen
ausgBinaerBaum(b.rechts, stufe + 1);
}
}
/*
* Rueckgabe: Hoehe des Knotens, oder -1, falls null.
*/
private static int hoehe(AvlKnoten b)
{
return b == null ? -1 : b.hoehe;
}
/*
* Rueckgabe: Maximum aus lhs und rhs.
*/
private static int max( int lhs, int rhs )
{
return lhs > rhs ? lhs : rhs;
}
/*
* Rotation Binaerbaumknoten mit linkem Nachfolger.
* Fuer AVL-Baeume ist dies eine einfache Rotation (Fall 1).
* 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;
}
/*
* Rotate binary tree node with right child.
* For AVL trees, this is a single rotation for case 4.
* Update heights, then return new wurzel.
*/
private static AvlKnoten rotationMitRechtsNachf(AvlKnoten k1)
{
AvlKnoten k2 = k1.rechts;
k1.rechts = k2.links;
k2.links = k1;
k1.hoehe = max( hoehe( k1.links ), hoehe( k1.rechts ) ) + 1;
k2.hoehe = max( hoehe( k2.rechts ), k1.hoehe ) + 1;
return k2;
}
/*
* Double rotate binary tree node: first left child
* with its right child; then node k3 with new left child.
* For AVL trees, this is a double rotation for case 2.
* Update heights, then return new wurzel.
*/
private static AvlKnoten doppelrotationMitLinksNachf(AvlKnoten k3)
{
k3.links = rotationMitRechtsNachf( k3.links );
return rotationMitLinksNachf( k3 );
}
/*
* Double rotate binary tree node: first right child
* with its left child; then node k1 with new right child.
* For AVL trees, this is a double rotation for case 3.
* Update heights, then return new wurzel.
*/
private static AvlKnoten doppelrotationMitRechtsNachf(AvlKnoten k1)
9
Algorithmen und Datenstrukturen
{
k1.rechts = rotationMitLinksNachf(k1.rechts);
return rotationMitRechtsNachf(k1);
}
}
public class AvlBaumTest
{
// Test-Programm
public static void main(String [] args)
{
// Test Nr. 1
AvlBaum b = new AvlBaum();
final int ZAHLEN = 4000;
final int LUECKE = 37;
System.out.println(
"Pruefung... (keine weiteren Ausgaben bedeutet Erfolg)");
for( int i = LUECKE; i != 0; i = (i + LUECKE) % ZAHLEN)
b.insert( new Integer( i ) );
for(int i = 1; i < ZAHLEN; i+= 2 )
b.remove( new Integer( i ) );
if (ZAHLEN < 40)
b.printTree( );
if ( ((Integer)(b.findMin( ))).intValue( ) != 2 ||
((Integer)(b.findMax( ))).intValue( ) != ZAHLEN - 2 )
System.out.println( "FindMin oder FindMax -Fehler!" );
for( int i = 2; i < ZAHLEN; i+=2 )
if( ((Integer)(b.find( new Integer( i ) ))).intValue( ) != i )
System.out.println( "Find Fehler Nr.1!" );
for( int i = 1; i < ZAHLEN; i+=2 )
{
if( b.find( new Integer( i ) ) != null )
System.out.println( "Find Fehler Nr.2!" );
}
// Test Nr.2
AvlBaum b1 = new AvlBaum();
for (int i = 0; i < 10; i++)
{
// Erzeuge eine Zahl zwischen 0 und 100
Integer r = new Integer((int)(Math.random()*100));
b1.insert(r);
}
System.out.println("Inorder-Durchlauf");
b1.printTree();
System.out.println();
System.out.println("Baumdarstellung um 90 Grad versetzt");
b1.ausgBinaerBaum();
System.out.print("Kleinster Wert: ");
System.out.print(((Integer)(b1.findMin())).intValue());
System.out.println();
System.out.print("Groesster Wert: ");
System.out.print(((Integer)(b1.findMax())).intValue());
System.out.println();
for (int i = 0; i < 10; i++)
{
// Erzeuge eine Zahl zwischen 0 und 100
Integer r = new Integer((int)(Math.random()*100));
if ( b1.find(r) != null )
{
b1.remove( r );
}
// else System.out.println(r.intValue() + " nicht gefunden");
}
10
Algorithmen und Datenstrukturen
b1.ausgBinaerBaum();
// Test Nr. 3
AvlBaum b2 = new AvlBaum();
for (int i = 0; i < 20; i++) // 20 Zusfallsstrings speichern
{
String s = "Zufallszahl " + (int)(Math.random() * 100);
b2.insert(s);
}
b2.printTree(); // Sortiert wieder ausdrucken
// Test Nr.4
AvlBaum b3 = new AvlBaum();
for (int i = 0; i < 20; i++) //
{
Integer r = new Integer(i);
b3.insert(r);
}
b3.ausgBinaerBaum();
}
}
11
Herunterladen