Übung 6. Rot-Schwarz-Bäume - oth

Werbung
Algorithmen und Datenstrukturen
Übung 6. Rot-Schwarz-Bäume
Top-Down 2.-3-4-Bäume
Zum Ausschluß des ungünstigsten Falls bei binären Suchbäumen ist eine gewisse Flexibilität in den
verwendeten Datenstrukturen nötig. Das kann bspw. durch Aufnahme von mehr als einem Schlüssel
in Baumknoten erreicht werden. So soll es 3-Knoten bzw. 4-Knoten geben, die 2 bzw. 3 Schlüssel
enthalten können:
- Ein 3-Knoten besitzt 3 von ihm ausgehende Verkettungen
-- eine für alle Datensätze mit Schlüsseln, die kleiner sind als seine beiden Schlüssel
-- eine für alle Datensätze, die zwischen den beiden Schlüsseln liegen
-- eine für alle Datensätze mit Schlüsseln, die größer sind als seine beiden Schlüssel.
- Ein 4-Knoten besitzt vier von ihm ausgehende Verkettungen, nämlich eine Verkettung für jedes der
Intervalle, die durch seine drei Schlüssel definiert werden.
Rot-Schwarz-Bäume
Es ist möglich 2 -3-4-Bäume als gewöhnliche binäre Bäume (mit nur zwei Knoten) darzustellen, wobei
nur ein zusätzliches Bit je Knoten verwendet wird. Die Idee besteht darin, 3-Knoten und 4 -Knoten als
kleine binäre Bäume darzustellen, die durch „rote“ Verkettungen miteinander verbunden sind, im
Gegensatz zu den schwarzen Verkettungen, die den 2 -3-4-Baum zusammenhalten:
oder
Abb.: Rot-schwarze Darstellung von Bäumen
4-Knoten werden als 2-Knoten dargestellt, die mittels einer roten Verkettung verbunden sind.
3-Knoten werden als 2 –Knoten dargestellt, die mit einer roten Markierung verbunden sind.
Zu jedem 2-3-4-Baum gibt es viele ihm entsprechende Rot-Schwarz-Bäume.
1
Algorithmen und Datenstrukturen
Eine Variante von Rot-Schwarz-Bäumen
Nach jeder Einfüge -Operation bzw. Entferne-Operation lassen sich Rot-Schwarz-Bäume durch
Rotationen ausgleichen.
Eine Variante von Rot-Schwarz-Bäumen ist durch folgende Farbeigenschaften definiert:
1. Jeder Knoten ist entweder rot oder schwarz gefärbt.
2. der Wurzelknoten ist schwarz gefärbt.
3. Falls ein Knoten rot gefärbt ist, müssen seine Nachfolger schwarz gefärbt sein.
4. Jeder Pfad von einem Knoten zu einer „Null-Referenz“ muß die gleiche Anzahl von schwarzen
Knoten enthalten.
Eine Folgerung dieser Farbregeln ist: Die Höhe eines Rot-Schwarz-Baums ist etwa
2 ⋅ log( N + 1) .
Aufgabe: Ermittle, welche Gestalt jeweils ein nach den vorliegenden Farbregeln erstellte Rot-SchwarzBaum beim einfügen folgenden Schlüssel „10 85 15 70 20 60 30 50 65 80 90 40 5 55“ annimmt
10
10
85
10
85
15
15
10
85
70
15
10
85
70
20
15
10
70
20
85
60
15
10
70
20
85
60
2
Algorithmen und Datenstrukturen
30
15
10
70
30
85
20
60
50
30
15
10
70
20
60
85
50
65
30
15
10
70
20
60
85
50
65
80
30
15
10
70
20
60
85
50
65
80
90
30
15
10
70
20
60
85
50
65
3
80
90
Algorithmen und Datenstrukturen
40
30
15
70
10
20
60
85
50
65
80
90
40
5
30
15
70
10
20
60
5
85
50
65
80
90
40
55
30
15
70
10
20
60
5
85
50
40
65
9
85
55
80
90
Die Abbildungen zeigen, daß im Durchschnitt der Rot-Schwarz-Baum ungefähr die Tiefe eines AVLBaums besitzt.
Der Vorteil von Rot-Schwarz-Bäumen ist der geringere Overhead zur Ausführung von
Einfügevorgängen und die geringere Anzahl von Rotationen.
„Top -Down“ Rot-Schwarz-Bäume
Kann auf dem Weg nach unten festgestellt werden, daß ein Knoten X zwei rote Nachfolgeknoten hat,
dann wird X rot und die beiden „Kinder“ schwarz:
X
c1
X
c2
c1
c2
Das führt zu einem Verstoß gegen Bedingung 3, falls der Vorgänger von X auch rot ist. In diesem Fall
können aber geeignete Rotationen herangezogen werden:
4
Algorithmen und Datenstrukturen
G
P
P
S
X
C
X
A
G
A
B
B
S
C
G
P
X
S
X
P
G
C
A
A
B1
B1
B2
B2
S
C
Der folgende Fall kann nicht eintreten: Der Nachbar vom Elternknoten ist rot gefärbt. Auf dem Weg
nach unten muß der Knoten mit zwei roten Nachfolgern einen schwarzen Großvaterknoten haben.
Implementierung
Sie wird dadurch erschwert, daß einige Teilbäume (z.B. der rechte Teilbaum des Knoten 10 im
vorliegenden Bsp.) leer sein können, und die Wurzel des Baums (, da ohne Vorgänger, ) einer
speziellen Behandlung bedarf. Aus diesem Grund werden hier „Sentinel“-Knoten verwendet:
- einer für die Wurzel. Dieser Knoten speichert den Schlüssel − ∞ und einen rechten Link zu dem
realen Knoten
- einen Nullknoten (nullNode), der eine Null-Referenz anzeigt.
Der Inorder-Durchlauf nimmt aus diesem Grund folgende Gestalt an:
/*
Ausgabe der im Baum gespeicherten Datenelemente in sortierter
Reihenfolge.
*/
public void printTree( )
{
if( isEmpty() )
System.out.println("Leerer Baum");
else
printTree( header.rechts );
}
/*
* Interne Methode fuer die Ausgabe des Baum in sortierter Reihenfolge.
* b ist der Wurzelknoten.
*/
private void printTree(RotSchwarzKnoten b)
{
if (b != nullNode )
{
printTree(b.links);
System.out.println(b.daten );
printTree(b.rechts );
}
}
5
Algorithmen und Datenstrukturen
Programme: 1
// BaumKnoten fuer RotSchwarzBaeume
class RotSchwarzKnoten
{
// Instanzvariable
public Comparable daten;
// Dateninformation der Knoten
protected RotSchwarzKnoten links; // linkes Kind
protected RotSchwarzKnoten rechts; // rechtes Kind
protected int farbe;
// Farbe
// Konstruktoren
RotSchwarzKnoten(Comparable datenElement)
{
this(datenElement, null, null );
}
RotSchwarzKnoten(Comparable datenElement,
RotSchwarzKnoten l, RotSchwarzKnoten r)
{
daten = datenElement;
links
= l;
rechts
= r;
farbe
= RotSchwarzBaum.BLACK;
}
}
//
//
//
//
//
//
//
//
//
//
//
//
//
//
Die Klasse RotSchwarzBaum
Konstruktion: mit einem "negative infinity sentinel"
******************PUBLIC OPERATIONEN*********************
void insert(x)
--> Insert x
void remove(x)
--> Entferne x (nicht implementiert)
Comparable find(x)
--> Rueckgabe 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 ausgRotSchwarzBaum() --> Ausgabe des Baums um 90 Grad versetzt
/*
* Implementierung eines RotSchwarzBaum.
* Vergleiche basieren auf der Methode compareTo.
*/
public class RotSchwarzBaum
{
private RotSchwarzKnoten header;
private static RotSchwarzKnoten nullNode;
static
// Static Initialisierer for nullNode
{
nullNode = new RotSchwarzKnoten(null);
nullNode.links = nullNode.rechts = nullNode;
}
static final int BLACK = 1;
// Black must be 1
static final int RED
= 0;
// Fuer "insert routine" und zugehoerige unterstuetzende Routinen
private static RotSchwarzKnoten current;
private static RotSchwarzKnoten parent;
private static RotSchwarzKnoten grand;
1 PR43220
6
Algorithmen und Datenstrukturen
private static RotSchwarzKnoten great;
/*
* Baumkonstruktion.
* negInf ist ein Wert, der kleiner oder gleich zu allen anderen Werten
ist.
*/
public RotSchwarzBaum(Comparable negInf)
{
header = new RotSchwarzKnoten(negInf);
header.links = header.rechts = nullNode;
}
/*
* Einfügen in den Baum. Duplikate werden ueberlesen.
* "item" ist das einzufuegende Datenelement.
*/
public void insert(Comparable item)
{
current = parent = grand = header;
nullNode.daten = item;
while( current.daten.compareTo( item ) != 0 )
{
great = grand; grand = parent; parent = current;
current = item.compareTo(current.daten ) < 0 ?
current.links : current.rechts;
// Pruefe, ob zwei rote Kinder; falls es so ist, fixiere es
if( current.links.farbe == RED && current.rechts.farbe == RED )
reOrientierung( item );
}
// Fehlanzeige fuer Einfuegen, falls schon da
if( current != nullNode )
return;
current = new RotSchwarzKnoten( item, nullNode, nullNode );
// Anhaengen an die Eltern
if( item.compareTo( parent.daten ) < 0 )
parent.links = current;
else
parent.rechts = current;
reOrientierung( item );
}
/*
* Entferne aus dem Baum.
* Nicht implementiert
* x ist das zu entfernemde Datenelement.
*/
public void remove( Comparable x )
{
System.out.println("Entfernen ist nicht implementiert");
}
/*
* Finde das kleinste Datenelement im Baum.
* Rueckgabe kleinstes Datenelement oder null, falls leer.
*/
public Comparable findMin( )
{
if (isEmpty( ))
return null;
RotSchwarzKnoten itr = header.rechts;
while( itr.links != nullNode )
itr = itr.links;
return itr.daten;
}
/*
* Finde das groesste Datenelement im Baum.
7
Algorithmen und Datenstrukturen
* Rueckgabe des groessten Datenelemnts oder null, falls leer.
*/
public Comparable findMax( )
{
if (isEmpty( ))
return null;
RotSchwarzKnoten itr = header.rechts;
while( itr.rechts != nullNode )
itr = itr.rechts;
return itr.daten;
}
/*
* Finde ein Datenelement im Baum.
* x enthaelt das zu suchende Datenelement.
* Rueckgabe des passenden Datenelements oder null, falls nicht gefunden.
*/
public Comparable find(Comparable x)
{
nullNode.daten = x;
current = header.rechts;
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;
}
}
/*
* Mache den Baum logisch leer.
*/
public void makeEmpty( )
{
header.rechts = nullNode;
}
/*
* Test, ob der Baum logisch leer ist.
* Rueckgabe true, falls leer; anderenfalls false.
*/
public boolean isEmpty( )
{
return header.rechts == nullNode;
}
/*
* Ausgabe der im Baum gespeicherten Datenelemente in sortierter
Reihenfolge.
*/
public void printTree( )
{
if( isEmpty() )
System.out.println("Leerer Baum");
else
printTree( header.rechts );
}
/*
* Interne Methode fuer die Ausgabe des Baum in sortierter Reihenfolge.
* b ist der Wurzelknoten.
*/
private void printTree(RotSchwarzKnoten b)
8
Algorithmen und Datenstrukturen
{
if (b != nullNode )
{
printTree(b.links);
System.out.println(b.daten );
printTree(b.rechts );
}
}
/*
* Ausgabe des Binaerbaums um 90 Grad vesetzt
*/
public void ausgRotSchwarzBaum()
{
ausgRotSchwarzBaum(header.rechts,0);
}
private void ausgRotSchwarzBaum(RotSchwarzKnoten b, int nSpace)
{
if (b != nullNode)
{
ausgRotSchwarzBaum(b.links,nSpace += 6);
for (int i = 0; i < nSpace; i++)
System.out.print(" ");
System.out.println(b.daten + " " + b.farbe);
ausgRotSchwarzBaum(b.rechts, nSpace);
}
}
/*
* Interne Routine, die waehrend eines Einfuegevorgangs aufgerufen wird
* Falls ein Knoten zwei rote Kinder hat, fuehre Tausch der Farben aus
* und rotiere.
* item enthaelt das einzufuegende Datenelement.
*/
private void reOrientierung(Comparable item)
{
// Tausch der Farben
current.farbe = RED;
current.links.farbe = BLACK;
current.rechts.farbe = BLACK;
if (parent.farbe == RED)
// Rotation ist noetig
{
grand.farbe = RED;
if ( (item.compareTo( grand.daten) < 0 ) !=
(item.compareTo( parent.daten) < 0 ) )
parent = rotation(item, grand); // Start Doppelrotation
current = rotation(item, great);
current.farbe = BLACK;
}
header.rechts.farbe = BLACK; // Mache die Wurzel schwarz
}
/*
* Interne Routine, die eine einfache oder doppelte Rotation veranlasst.
* Da das Ergebnis an "parent" geheftet wird, gibt es vier Faelle.
* Aufruf durch reOrientierung.
* "item" ist das Datenelement reOrientierung.
* "parent" ist "parent" von der wurzel des rotierenden Teilbaums.
* Rueckgabe: Wurzel des rotierenden Teilbaums.
*/
private RotSchwarzKnoten rotation(Comparable item, RotSchwarzKnoten
parent)
{
if (item.compareTo(parent.daten) < 0)
return parent.links = item.compareTo( parent.links.daten) < 0 ?
rotationMitLinksNachf(parent.links) : // LL
9
Algorithmen und Datenstrukturen
rotationMitRechtsNachf(parent.links) ;
// LR
else
return parent.rechts = item.compareTo(parent.rechts.daten) < 0 ?
rotationMitLinksNachf(parent.rechts) : // RL
rotationMitRechtsNachf(parent.rechts); // RR
}
/*
* Rotation Binaerbaumknoten mit linkem Nachfolger.
*/
static RotSchwarzKnoten rotationMitLinksNachf( RotSchwarzKnoten k2 )
{
RotSchwarzKnoten k1 = k2.links;
k2.links = k1.rechts;
k1.rechts = k2;
return k1;
}
/*
* Rotate Binaerbaumknoten mit rechtem Nachfolger.
*/
static RotSchwarzKnoten rotationMitRechtsNachf(RotSchwarzKnoten k1)
{
RotSchwarzKnoten k2 = k1.rechts;
k1.rechts = k2.links;
k2.links = k1;
return k2;
}
}
import java.io.*;
public class RotSchwarzBaumTest
{
public static void main(String[ ] args)
{
InputStreamReader isr = new InputStreamReader(System.in);
BufferedReader
ein = new BufferedReader(isr);
String eingabeZeile
= null;
RotSchwarzBaum b = new RotSchwarzBaum(new Integer(Integer.MIN_VALUE));
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.ausgRotSchwarzBaum();
}
catch(IOException e)
{
System.out.println(e.toString());
// System.exit(0);
}
}
// b.printTree();
b.ausgRotSchwarzBaum();
}
}
10
Herunterladen