ueb7 - oth-regensburg.de

Werbung
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.
1
Algorithmen und Datenstrukturen
10
10
85
10
85
15
10
15
85
„skew“
15
„slip“
10
85
70
15
10
70
20
85
15
70
10
20
60
15
10
85
70
20
30
60
85
15
10
70
20
60
30
2
85
Algorithmen und Datenstrukturen
15
10
30
70
20
60
15
85
30
10
20
70
60
85
30
15
10
70
20
50
60
85
30
15
10
70
20
65
50
60
85
30
15
10
60
20
3
50
70
65
85
Algorithmen und Datenstrukturen
80
30
15
10
60
20
90
70
50
65
30
70
15
10
60
20
40
50
85
65
30
80
60
20
90
70
15
10
80
40
4
50
85
65
80
90
85
Algorithmen und Datenstrukturen
5
30
70
15
5
60
10
55
20
40
85
50
65
30
10
50
20
35
40
60
55
85
65
30
10
50
20
80
90
70
15
5
90
70
15
5
80
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;
}
}
5
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;
}
6
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
7
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
8
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
9
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;
}
10
Algorithmen und Datenstrukturen
Lösungen
1
// 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;
}
}
// Die Klasse AABaum
//
// KONSTRUKTION: ohne Initialisierer
//
// ******************PUBLIC OPERATIONEN*********************
// void insert(x)
--> Insert x
// void remove(x)
--> Entferne x
// Comparable find(x)
--> Ruecggabe des Datenelements, das x enthaelt
// Comparable findMin() --> Rueckgabe des kleinsten Datenelements
// Comparable findMax() --> Rueckgabe des groessten Datenelements
// boolean isEmpty()
--> Rueckgabe true, falls leer; anderenfalls false
// void makeEmpty()
--> Entferne alles
// void printTree()
--> Ausgabe in aufsteigend sortierter Folge
// void ausgAABaum()
--> Ausgabe des Baums um 90 Grad versetzt
/*
* Implementiert einen AA-Baum
* Vergleichsoperationen benutzen die Methode compareTo().
*/
public class AABaum
{
private AAKnoten wurzel;
private static AAKnoten nullNode;
static
// static Initialisierer fuer "nullNode"
{
nullNode = new AAKnoten(null);
nullNode.links = nullNode.rechts = nullNode;
nullNode.level = 0;
}
private static AAKnoten deletedNode;
private static AAKnoten lastNode;
/*
* Konstruktor.
*/
public AABaum( )
{
wurzel = nullNode;
1
pr43225
11
Algorithmen und Datenstrukturen
}
/*
* Einfuegen in den Baum. Duplikate bleiben unberuecksichtigt.
* x ist das einzufuegende Element.
*/
public void insert(Comparable x)
{
wurzel = insert(x, wurzel);
}
/*
* Entfernen aus dem Baum. Tut nichts, falls "x" nicht gefunden wurde.
* x ist das zu entfernende Element.
*/
public void remove( Comparable x )
{
deletedNode = nullNode;
wurzel = remove(x, wurzel);
}
/*
* Finde das kleinste Datenelement im Baum.
* Rueckgabe: kleinstes Datenelement oder null, falls leer.
*/
public Comparable findMin( )
{
if (isEmpty( ))
return null;
AAKnoten zgr = wurzel;
while (zgr.links != nullNode )
zgr = zgr.links;
return zgr.daten;
}
/*
* Finde das groesste Datenelement im Baum.
* Rueckgabe: das groesste Datenelement oder null, falls leer.
*/
public Comparable findMax( )
{
if (isEmpty( ))
return null;
AAKnoten zgr = wurzel;
while (zgr.rechts != nullNode)
zgr = zgr.rechts;
return zgr.daten;
}
/*
* Finde ein Datenelement im Baum.
* "x" ist das zu suchende Datenelement.
* Rueckgabe des gefundenen Datenelements oder null, falls nicht gefunden.
*/
public Comparable find(Comparable x )
{
AAKnoten current = wurzel;
nullNode.daten = x;
for( ; ; )
{
if (x.compareTo(current.daten) < 0 )
current = current.links;
else if (x.compareTo(current.daten) > 0 )
current = current.rechts;
else if (current != nullNode)
return current.daten;
else
return null;
12
Algorithmen und Datenstrukturen
}
}
/*
* Mache den Baum logisch leer.
*/
public void makeEmpty()
{
wurzel = nullNode;
}
/*
* Test. ob der Baum logisch leer ist.
* Rueckgabe true, falls leer; anderenfalls false.
*/
public boolean isEmpty( )
{
return wurzel == nullNode;
}
/*
* Ausgabe der Bauminhalts in sortierter Reihenfolge.
*/
public void printTree()
{
if (isEmpty( ))
System.out.println("Leerer Baum");
else
printTree(wurzel);
}
/*
* Ausgabe des Binaerbaums um 90 Grad vesetzt
*/
public void ausgAABaum()
{
ausgAABaum(wurzel,0);
}
/*
* 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;
}
/*
* 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 )
13
Algorithmen und Datenstrukturen
{
// 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;
}
/*
* Interne Methode zur Ausgabe des baums in sortierter Folge.
* "b" ist die Wurzel.
*/
private void printTree(AAKnoten b)
{
if (b != b.links)
{
printTree( b.links );
System.out.println( b.daten.toString( ) );
printTree(b.rechts );
}
}
private void ausgAABaum(AAKnoten b, int nSpace)
{
if (b != b.links)
{
ausgAABaum(b.links,nSpace += 6);
for (int i = 0; i < nSpace; i++)
System.out.print(" ");
System.out.println(b.daten + ", " + b.level);
ausgAABaum(b.rechts, nSpace);
}
}
/*
* Skew Primitive fuer AA-Baeume.
* "b" ist die Wurzel.
* Rueckgabe: die neue wurzel nach der Rotation.
14
Algorithmen und Datenstrukturen
*/
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;
}
/*
* Rotation des Binaerbaumknotens mit dem linken Nachfolger.
*/
static AAKnoten rotationMitLinksNachf(AAKnoten k2)
{
AAKnoten k1 = k2.links;
k2.links = k1.rechts;
k1.rechts = k2;
return k1;
}
/*
* Rotation Binaerbaumknoten mit rechtem Nachfolger.
*/
static AAKnoten rotationMitRechtsNachf(AAKnoten k1)
{
AAKnoten k2 = k1.rechts;
k1.rechts = k2.links;
k2.links = k1;
return k2;
}
}
import java.io.*;
public class AABaumTest
{
public static void main(String[ ] args)
{
InputStreamReader isr = new InputStreamReader(System.in);
BufferedReader
ein = new BufferedReader(isr);
String eingabeZeile
= null;
AABaum b = new AABaum();
System.out.println("Eingabe ganzzahliger Werte: ");
while (true)
{
try {
eingabeZeile = ein.readLine();
int a = Integer.parseInt(eingabeZeile);
if (a == 0) break;
b.insert(new Integer(a));
b.ausgAABaum();
}
15
Algorithmen und Datenstrukturen
catch(IOException e)
{
System.out.println(e.toString());
// System.exit(0);
}
}
// b.printTree();
b.ausgAABaum();
}
}
16
Herunterladen