ueb10 - oth

Werbung
Algorithmen und Datenstrukturen
Übung 10: Auf Platte / Diskette gespeicherte Bayer-Bäume und B*-Bäume
Implementierung eines auf Platte gespeicherten Bayer-Baums in C++1
Die Klasse BBaum
Sie übernimmt die Verwaltung des auf einer Datei hinterlegten Bayer-Baums:
/* Programm fuer einen auf Platte (Disk) gespeicherten Bayer-Baum:
Die Eingabedaten sind ganzzahlige Schlüssel. Sie koennen ueber
Konsole oder ueber eine Textdatei eingegeben werden. Einfuegen
und Loeschen kann direkt ueber die Konsole erfolgen. Weiterhin
kann nach einem Schluessel gesucht werden. Nach jeder Operation
wird der Baum bzw. der Suchpfad ausgegeben. Das Programm schreibt
die Daten in eine Binaer-Datei auf der Platte. Der Name der Datei
kann ueber die Konsole eingegeben werden. Falls die Datei schon
Daten im Bayer-Baum Format enthaelt, wird dieser Bayer-Baum
weiterhin benutzt. Im anderen Fall wird eine solche Datei erzeugt.
*/
#include
#include
#include
#include
<fstream.h>
<iomanip.h>
<stdlib.h>
<ctype.h>
#define MAERER 5
// Anzahl Verkettungen im Bayer-Baum Knoten
enum status {unvollstaendigesEinfuegen, erfolgreich, doppelterSchluessel,
Unterlauf, nichtGefunden};
typedef int dtype;
struct knoten {
int n;
//
//
dtype s[MAERER-1]; //
long z[MAERER];
//
Anzahl der Elemente, die in einem Knoten
gespeichert sind (n < MAERER)
Datenelemente (aktuell sind n)
'Zeiger' auf andere Knoten (aktuell sind n+1)
};
// Logische Ordnung:
//
z[0], s[0], z[1], s[1], ..., z[n-1], s[n-1], z[n]
// Die Klasse zum auf einer Datei hinterlegten Bayer-Baum
class BBaum {
private:
enum {NIL=-1};
long wurzel, freieListe;
knoten wurzelKnoten;
fstream datei;
status einf(long w, dtype x, dtype &y, long &u);
void ausg(long w, int nLeer);
int knotenSuche(dtype x, const dtype *a, int n)const;
status loe(long w, dtype x);
void leseKnoten(long w, knoten &Knoten);
void schreibeKnoten(long w, const knoten &Knoten);
void leseStart();
long holeKnoten();
void freierKnoten(long w);
public:
BBaum(const char *BaumDateiname);
1
Vgl. PR44_2
1
Algorithmen und Datenstrukturen
~BBaum();
void einfuegen(dtype x);
void einfuegen(const char *eingabeDateiname);
void gibAus(){cout << "Dateninhalt:\n"; ausg(wurzel, 0);}
void loeschen(dtype x);
void zeigeSuche(dtype x);
};
Zur Verwaltung des Bayer-Baums in einer Datei ist besonders wichtig:
- Die Wurzel wurzel (d. h. die Position des Wurzelknotens
- eine Liste mit Informationen über den freien Speicherplatz in der Datei (adressiert über
freieListe).
Zu solchen freien Speicherplätzen kann es beim Löschen von Schlüsselelementen kommen.
Zweckmäßigerweise wird dann dieser freie Speicherbereich nicht aufgefüllt, sondern in einer Liste
freier Speicherbereiche eingekettet. freieListe zeigt auf das erste Element in dieser Liste.
Solange das Programm läuft sind „wurzel“ und „freieListe“ Datenelemente der Klasse BBaum
(in einer Datei abgelegter Bayer-Baum). Für die Belegung dieser Dateielemente wird am
Dateianfang (1.Seite) Speicherplatz reserviert. Am Ende der Programmausführung werden die
Werte zu „wurzel“ bzw. „freieListe“ in der Datei abgespeichert.
Bsp.: Der folgende Bayer-Baum
20
10
15
60
80
ist bspw. folgendermaßen organisiert:
10
wurzel
15
60
80
20
freieListe
Zeiger haben hier ganzzahlige Werte des Typs long (mit -1L als NIL), die für die Positionen und
Bytenummern stehen.
Erzeugen des Bayer-Baums (Konstruktor)
BBaum::BBaum(const char *BaumDateiname)
{
ifstream test(BaumDateiname, ios::in | ios::nocreate);
int NewFile = test.fail();
test.clear(); test.close();
if (NewFile)
{
datei.open(BaumDateiname, ios::out | ios::in |
ios::trunc | ios::binary);
wurzel = freieListe = NIL;
long start[2] = {NIL, NIL};
datei.write((char*)start, 2 * sizeof(long));
}
else
{
2
Algorithmen und Datenstrukturen
long start[2];
datei.open(BaumDateiname, ios::out | ios::in |
ios::nocreate | ios::binary);
datei.seekg(-1L, ios::end);
char zeichen;
datei.read(&zeichen, 1);
datei.seekg(0L, ios::beg);
datei.read((char *)start, 2 * sizeof(long));
if (zeichen != sizeof(int))
{ cout << "Falsches Dateiformat.\n"; exit(1);
}
wurzel = start[0];
freieListe = start[1];
wurzelKnoten.n = 0;
// Signal fuer Funktion leseKnoten
leseKnoten(wurzel, wurzelKnoten);
gibAus();
}
}
Einfügen
void BBaum::einfuegen(dtype x)
{ long zNeu;
dtype xNeu;
status code = einf(wurzel, x, xNeu, zNeu);
if (code == doppelterSchluessel)
cout << "Doppelter Schluessel wurde ignoriert.\n";
if (code == unvollstaendigesEinfuegen)
{ long wurzel0 = wurzel;
wurzel = holeKnoten();
wurzelKnoten.n = 1; wurzelKnoten.s[0] = xNeu;
wurzelKnoten.z[0] = wurzel0; wurzelKnoten.z[1] = zNeu;
schreibeKnoten(wurzel, wurzelKnoten);
}
}
Löschen
void BBaum::loeschen(dtype x)
{
long wurzel0;
switch (loe(wurzel, x))
{
case nichtGefunden:
cout << x << " nicht gefunden.\n";
break;
case Unterlauf:
wurzel0 = wurzel;
wurzel = wurzelKnoten.z[0]; freierKnoten(wurzel0);
if (wurzel != NIL) leseKnoten(wurzel, wurzelKnoten);
break;
}
}
3
Algorithmen und Datenstrukturen
B*-Bäume
Der B*-Baum entspricht einer geketteten sequentiellen Datei von Blättern, die einen
Indexteil besitzt, der selbst ein B-Baum ist. Im Indexteil werden insbesondere beim
Spli-Vorgang die Operationen des B-Baums eingesetzt.
Hauptunterschied zu B-Bäumen: im inneren Knoten wird nur die Wegweiser-Funktion
ausgenutzt:
- innere Knoten führen nur (Si,Zi) als Einträge.
- Information (Si,Di) wird in den Blattknoten abgelegt. Dabei werden alle Schlüssel mit ihren
zugehörigen Daten in Sortierreihenfolge in den Blättern abgelegt.
- Für einige Si ergibt sich redundante Speicherung. Die inneren Knoten bilden einen Index, der einen
schnellen direkten Zugriff zu den Schlüsseln ermöglicht.
- Durch Verkettung aller Blattknoten lässt sich eine effiziente sequentielle Verarbeitung erreichen
. 12 .
.2.5.9.
1 2
3 4 5
6 7 8 9
Abb.: B*-Baum der Klasse
. 15 . 18 . 20 .
10 11 12
13 14 15
16 17 18
19 20
21 22 23
 ( 2,2,3)
Definition. k, k* >0 und h* >= 0 sind ganze Zahlen. Ein B*-Baum der Klasse
 (k , k *, h*) ist entweder ein leerer Baum oder ein geordneter Baum, für den gilt:
1. Jeder Pfad von der Wurzel zu einem Blatt besitzt die gleiche Länge h* - 1.
2. Jeder Knoten außer der Wurzel und den Blättern hat mindestens k + 1 Söhne, die Wurzel hat
mindestens 2 Söhne, außer wenn sie ein Blatt ist.
3. Jeder innere Knoten hat höchstens 2k + 1 Söhne.
4. Jeder Blattknoten mit Ausnahme der Wurzel als Blatt hat mindestens k* und höchstens 2k* Einträge
Unterscheidung zwischen zwei Knotenformaten:
l
innere Knoten
k <=b <=2k
M . S1 .
Z0
Blattknoten
k*<=m<=2k*
.........
Z1
Zb
M . . S1D1 S2D2
Zp
Sb . freier Platz
....
SmDm
freier Platz
Zn
M: enthält Kennung des Seitentyps sowie Zahl der aktuellen Einträge
Abb. Knotenformate im B*-Baum
Bestimmen von k bzw. k*
4
Algorithmen und Datenstrukturen
l  l M  l z  2  k  (l z  l S ) , k 
l  lM  l z
2  (l S l z )
l  lM  2  l z
2  (l S l D )
l  l M  2  l z  2  k * (l S  l D ) ; k* 
Höhe des B*-Baums: 1  log 2 k 1 (
n
n
)  h*  2  log k 1 (
) für h* >= 2
2k *
2k *
Minimale bzw. maximale Anzahl von Knoten.
nmin  2  k * (k  1) h*2
nmax  2  k * (2k  1) h*1
Operationen. B*-Baum entspricht einer geketteten sequentiellen Datei von Blättern,
die einen Indexteil besitzt, der selbst ein B-Baum ist. Im Indexteil werden
insbesondere beim Split-Vorgang, die Operationen des B-Baums eingesetzt.
Grundoperationen beim B*-Baum.
(1) Direkte Suche: Da alle Schlüssel in den Blättern sind, kostet jede direkte Such h*
Zugriffe. h* ist jedoch im Mittel kleiner als h in B-Bäumen.
(2) Sequentielle Suche: Sie erfolgt nach Aufsuchen des Linksaußen der Struktur
unter Ausnutzung der Verkettung der Blattseiten. Es sind zwar gegebenenfalls mehr
Blätter als beim B-Baum zu verarbeiten, doch da nur h*-1 innere Knoten aufzusuchen
sind, wird die sequentielle Suche effizienter ablaufen.
(3) Einfügen: Von Durchführung und Leistungsverhalten dem Einfügen von BBäumen sehr ähnlich. Bei inneren Knoten wird die Spaltung analog zum B-Baum
durchgeführt. Beim Split-Vorgang einer Basis-Seite muß gewährleistet sein, dass
jeweils der höchste Schlüssel einer Seite als Wegweiser in den Vaterknoten kopiert
werden.
S2k*
S1D1 .... Sk*Dk* Sk*+1Dk*+1 ... S2k*D2k* SD
Sk* S2k*
S1D1 … Sk*Dk*
Sk*+1Dk*+1 … SD … S2k*D2k*
Bsp.: In den folgenden B*-Baum soll der Schlüssel 45 (einschl. Datenteil) eingefügt werden.
5
Algorithmen und Datenstrukturen
12 28 46 67
1 5 9 12
15 19 28
33 37 41 46
53 59 67
71 83 99
41
12 28
1 5 9 12
15 19
46 67
28
33
37
Abb.: Einfügen in einen B*-Baum der Klasse
41
45 46
53 59 67
71
83 99
 (2,2,3)
(4) Löschen: Datenelemente werden immer von einem Blatt entfernt (keine komplexe
Fallunterscheidung wie beim B-Baum). Weiterhin muß beim Löschen eines
Schlüssels aus einem Blatt dieser Schlüssel nicht aus dem Indexteil entfernt werden,
er behält seine Funktion als Wegweiser.
Bsp.: Löschen der Schüssel 28, 41, 46 (einschl. der zugehörigen Datenteile) im zuletzt angegebenen
B*-Baum der Klasse  ( 2,2,3)
41
12 28
1 5 9 12
15 19
53 67
28
33
37
Abb.: Löschen in einen B*-Baum der Klasse
45 53
 ( 2,2,3)
6
59 67
71
83 99
Algorithmen und Datenstrukturen
Lösungen
/* Programm fuer einen auf Platte (Disk) gespeicherten Bayer-Baum:
Die Eingabedaten sind ganzzahlige Schlüssel. Sie koennen ueber
Konsole oder ueber eine Textdatei eingegeben werden. Einfuegen
und Loeschen kann direkt ueber die Konsole erfolgen. Weiterhin
kann nach einem Schluessel gesucht werden. Nach jeder Operation
wird der Baum bzw. der Suchpfad ausgegeben. Das Programm schreibt
die Daten in eine Binaer-Datei auf der Platte. Der Name der Datei
kann ueber die Konsole eingegeben werden. Falls die Datei schon
Daten im Bayer-Baum Format enthaelt, wird dieser Bayer-Baum
weiterhin benutzt. Im anderen Fall wird eine solche Datei erzeugt.
*/
#include
#include
#include
#include
<fstream.h>
<iomanip.h>
<stdlib.h>
<ctype.h>
#define MAERER 5
// Anzahl Verkettungen im Bayer-Baum Knoten
enum status {unvollstaendigesEinfuegen, erfolgreich, doppelterSchluessel,
Unterlauf, nichtGefunden};
typedef int dtype;
struct knoten {
int n;
// Anzahl der Elemente, die in einem Knoten
// Knoten gespeichert sind (n < MAERER)
dtype s[MAERER-1]; // Datenelemente (aktuell sind n)
long z[MAERER];
// 'Zeiger' auf andere Knoten (aktuell sind n+1)
};
// Logische Ordnung:
//
z[0], s[0], z[1], s[1], ..., z[n-1], s[n-1], z[n]
class BBaum {
private:
enum {NIL=-1};
long wurzel, freieListe;
knoten wurzelKnoten;
fstream datei;
status einf(long w, dtype x, dtype &y, long &u);
void ausg(long w, int nLeer);
int knotenSuche(dtype x, const dtype *a, int n)const;
status loe(long w, dtype x);
void leseKnoten(long w, knoten &Knoten);
void schreibeKnoten(long w, const knoten &Knoten);
void leseStart();
long holeKnoten();
void freierKnoten(long w);
public:
BBaum(const char *BaumDateiname);
~BBaum();
void einfuegen(dtype x);
void einfuegen(const char *eingabeDateiname);
void gibAus(){cout << "Dateninhalt:\n"; ausg(wurzel, 0);}
void loeschen(dtype x);
void zeigeSuche(dtype x);
};
7
Algorithmen und Datenstrukturen
BBaum::BBaum(const char *BaumDateiname)
{ ifstream test(BaumDateiname, ios::in | ios::nocreate);
int NewFile = test.fail();
test.clear(); test.close();
if (NewFile)
{ datei.open(BaumDateiname, ios::out | ios::in |
ios::trunc | ios::binary);
wurzel = freieListe = NIL;
long start[2] = {NIL, NIL};
datei.write((char*)start, 2 * sizeof(long));
} else
{ long start[2];
datei.open(BaumDateiname, ios::out | ios::in |
ios::nocreate | ios::binary);
datei.seekg(-1L, ios::end);
char zeichen;
datei.read(&zeichen, 1);
datei.seekg(0L, ios::beg);
datei.read((char *)start, 2 * sizeof(long));
if (zeichen != sizeof(int))
{ cout << "Falsches Dateiformat.\n"; exit(1);
}
wurzel = start[0]; freieListe = start[1];
wurzelKnoten.n = 0;
// Signal fuer Funktion leseKnoten
leseKnoten(wurzel, wurzelKnoten);
gibAus();
}
}
BBaum::~BBaum()
{ long start[2];
datei.seekp(0L, ios::beg);
start[0] = wurzel; start[1] = freieListe;
datei.write((char*)start, 2 * sizeof(long));
char zeichen = sizeof(int), zeichen1; // Signatur
datei.seekg(-1L, ios::end); datei.read(&zeichen1, 1);
if (zeichen1 != zeichen)
{ datei.seekp(0L, ios::end);
datei.write(&zeichen, 1);
} // andernfalls ist die Signatur schon gegenwaertig.
datei.close();
}
void BBaum::einfuegen(dtype x)
{ long zNeu;
dtype xNeu;
status code = einf(wurzel, x, xNeu, zNeu);
if (code == doppelterSchluessel)
cout << "Doppelter Schluessel wurde ignoriert.\n";
if (code == unvollstaendigesEinfuegen)
{ long wurzel0 = wurzel;
wurzel = holeKnoten();
wurzelKnoten.n = 1; wurzelKnoten.s[0] = xNeu;
wurzelKnoten.z[0] = wurzel0; wurzelKnoten.z[1] = zNeu;
schreibeKnoten(wurzel, wurzelKnoten);
}
}
void BBaum::einfuegen(const char *eingabeDateiname)
{ ifstream eingDatei(eingabeDateiname, ios::in | ios::nocreate);
if (eingDatei.fail())
{ cout << "Kann nicht oeffnen Eingabedatei: " << eingabeDateiname
<< endl;
8
Algorithmen und Datenstrukturen
return;
}
dtype x;
while(eingDatei >> x) einfuegen(x);
eingDatei.clear(); eingDatei.close();
}
status BBaum::einf(long w, dtype x, dtype &y, long &q)
{ // Fuege x in den aktuellen Knoten, adressiert durch x ein.
// Falls nicht vollstaendig erfolgreich, sind noch Ganzzahl y
// und Zeiger q einzufuegen.
// Rueckgabewert:
//
erfolgreich, doppelterSchluessel oder unvollstaendigesEinfuegen.
long zNeu, zFinal;
int i, j, n;
dtype xNeu, sFinal;
status code;
if (w == NIL){q = NIL; y = x; return unvollstaendigesEinfuegen;}
knoten Knoten, neuerKnoten;
leseKnoten(w, Knoten);
n = Knoten.n;
i = knotenSuche(x, Knoten.s, n);
if (i < n && x == Knoten.s[i]) return doppelterSchluessel;
code = einf(Knoten.z[i], x, xNeu, zNeu);
if (code != unvollstaendigesEinfuegen) return code;
// Einfuegen in einen Teilbaum war nicht vollstaendig erfolgreich;
// Versuche xNeu und zNeu in den aktuellen Knoten einzubringen:
if (n < MAERER - 1)
{ i = knotenSuche(xNeu, Knoten.s, n);
for (j=n; j>i; j--)
{ Knoten.s[j] = Knoten.s[j-1]; Knoten.z[j+1] = Knoten.z[j];
}
Knoten.s[i] = xNeu; Knoten.z[i+1] = zNeu; ++Knoten.n;
schreibeKnoten(w, Knoten); return erfolgreich;
}
// Der aktuelle Knoten ist voll (n == MAERER - 1) und wird geteilt.
// Reiche das Element s[h] in der Mitte der betrachteten Folge zurueck
// ueber Parameter y, damit es aufwaerts im Baum plaziert werden kann.
// Reiche auch einen Zeiger zum neu erzeugten Knoten zurueck (als
// Verweis) ueber den Parameter q
if (i == MAERER - 1) {sFinal = xNeu; zFinal = zNeu;} else
{ sFinal = Knoten.s[MAERER-2]; zFinal = Knoten.z[MAERER-1];
for (j=MAERER-2; j>i; j--)
{ Knoten.s[j] = Knoten.s[j-1]; Knoten.z[j+1] = Knoten.z[j];
}
Knoten.s[i] = xNeu; Knoten.z[i+1] = zNeu;
}
int h = (MAERER - 1)/2;
y = Knoten.s[h];
// y und q werden zu naechst hoeheren Stufe
q = holeKnoten();
// im Baum weitergeleitet
// Die Werte z[0],s[0],z[1],...,s[h-1],s[h] gehoeren zum
// linken Teil von s[h] und werden gehalten in *w:
Knoten.n = h;
// z[h+1],s[h+1],z[h+2],...,s[MAERER-2],z[MAERER-1],sFinal,zFinal
// gehoeren zum rechten Teil von s[h] und werden nach *q gebracht:
neuerKnoten.n = MAERER - 1 - h;
for (j=0; j < neuerKnoten.n; j++)
{ neuerKnoten.z[j] = Knoten.z[j + h + 1];
neuerKnoten.s[j] =
(j < neuerKnoten.n - 1 ? Knoten.s[j + h + 1] : sFinal);
}
neuerKnoten.z[neuerKnoten.n] = zFinal;
schreibeKnoten(w, Knoten); schreibeKnoten(q, neuerKnoten);
9
Algorithmen und Datenstrukturen
return unvollstaendigesEinfuegen;
}
void BBaum::ausg(long w, int nLeer)
{ if (w != NIL)
{ int i;
cout << setw(nLeer) << "";
knoten Knoten; leseKnoten(w, Knoten);
for (i=0; i < Knoten.n; i++) cout << Knoten.s[i] << " ";
cout << endl;
for (i=0; i <= Knoten.n; i++) ausg(Knoten.z[i], nLeer+8);
}
}
int BBaum::knotenSuche(dtype x, const dtype *a, int n)const
{ int mitte, links=0, rechts = n - 1;
if (x <= a[links]) return 0;
if (x > a[rechts]) return n;
while (rechts - links > 1)
{ mitte = (rechts + links)/2;
(x <= a[mitte] ? rechts : links) = mitte;
}
return rechts;
}
void BBaum::zeigeSuche(dtype x)
{ cout << "Suchpfad:\n";
int i, j, n;
long w = wurzel;
knoten Knoten;
while (w != NIL)
{ leseKnoten(w, Knoten);
n = Knoten.n;
for (j=0; j<Knoten.n; j++) cout << " " << Knoten.s[j];
cout << endl;
i = knotenSuche(x, Knoten.s, n);
if (i < n && x == Knoten.s[i])
{ cout << "Schluessel " << x << " wurde in Position " << i
<< " vom zuletzt angegebenen Knoten gefunden.\n";
return;
}
w = Knoten.z[i];
}
cout << "Schluessel " << x << " nicht gefunden.\n";
}
void BBaum::loeschen(dtype x)
{ long wurzel0;
switch (loe(wurzel, x))
{
case nichtGefunden:
cout << x << " nicht gefunden.\n";
break;
case Unterlauf:
wurzel0 = wurzel;
wurzel = wurzelKnoten.z[0]; freierKnoten(wurzel0);
if (wurzel != NIL) leseKnoten(wurzel, wurzelKnoten);
break;
}
}
status BBaum::loe(long w, dtype x)
{ if (w == NIL) return nichtGefunden;
10
Algorithmen und Datenstrukturen
knoten Knoten;
leseKnoten(w, Knoten);
int i, j, pivot, n = Knoten.n;
dtype *s = Knoten.s; // s[i] bedeutet Knoten.s[i]
const int nMin = (MAERER - 1)/2;
status code;
long *z = Knoten.z, zL, zR;
// z[i] bedeutet Knoten.z[i]
i = knotenSuche(x, s, n);
if (z[0] == NIL) // Handelt es sich um ein Blatt?
{ if (i == n || x < s[i]) return nichtGefunden;
// x == s[i]
for (j=i+1; j < n; j++)
{ s[j-1] = s[j]; z[j] = z[j+1];
}
Knoten.n--;
schreibeKnoten(w, Knoten);
return Knoten.n >= (w==wurzel ? 1 : nMin) ?
erfolgreich : Unterlauf;
}
// *w ist ein innerer Knoten, kein Blatt:
if (i < n && x == s[i])
{ // x wurde in einem inneren Knoten gefunden. Gehe zumlinken
// Nachfolger und folge auf diesem Wege auf einem Pfad bis
// zu einem Blatt unter Benutzung der rechtesten Verzweigung
long q = z[i], q1; int nq; knoten Knoten1;
for (;;)
{ leseKnoten(q, Knoten1);
nq = Knoten1.n; q1 = Knoten1.z[nq];
if (q1 == NIL) break;
q = q1;
}
// Tausche s[i] (= x) mit dem am weitesten rechts stehenden
// Element im Blatt:
s[i] = Knoten1.s[nq-1];
Knoten1.s[nq - 1] = x;
schreibeKnoten(w, Knoten); schreibeKnoten(q, Knoten1);
}
// Loesche x im Blatt des Teilbaums mit Wurzel z[i]:
code = loe(z[i], x);
if (code != Unterlauf) return code;
// Es gibt einen Unterlauf; leihe und, falls noetig, mische:
// Zu wenig datenelemente im Knoten *z[i]
knoten KnotenL, KnotenR;
if (i > 0)
{ pivot = i - 1; zL = z[pivot]; leseKnoten(zL, KnotenL);
if (KnotenL.n > nMin) // Borge vom linken Nachbarn
{ // s[pivot] zwischen zL und zR:
zR = z[i];
// Erhoehe den Inhalt von *zR, geborgt von *zL:
leseKnoten(zR, KnotenR);
KnotenR.z[KnotenR.n + 1] = KnotenR.z[KnotenR.n];
for (j=KnotenR.n; j>0; j--)
{ KnotenR.s[j] = KnotenR.s[j-1];
KnotenR.z[j] = KnotenR.z[j-1];
}
KnotenR.n++;
KnotenR.s[0] = s[pivot];
KnotenR.z[0] = KnotenL.z[KnotenL.n];
s[pivot] = KnotenL.s[--KnotenL.n];
schreibeKnoten(zL, KnotenL); schreibeKnoten(zR, KnotenR);
schreibeKnoten(w, Knoten);
return erfolgreich;
}
11
Algorithmen und Datenstrukturen
}
pivot = i;
if (i < n)
{ zR = z[pivot+1]; leseKnoten(zR, KnotenR);
if (KnotenR.n > nMin) // Borge vom linken Nachbarn
{ // s[pivot] zwischen zL und zR:
zL = z[pivot]; leseKnoten(zL, KnotenL);
// Erhoehe den Inhalt von *zL, geborgt von *zR:
KnotenL.s[KnotenL.n] = s[pivot];
KnotenL.z[KnotenL.n + 1] = KnotenR.z[0];
s[pivot] = KnotenR.s[0];
KnotenL.n++; KnotenR.n--;
for (j=0; j < KnotenR.n; j++)
{ KnotenR.s[j] = KnotenR.s[j+1];
KnotenR.z[j] = KnotenR.z[j+1];
}
KnotenR.z[KnotenR.n] = KnotenR.z[KnotenR.n + 1];
schreibeKnoten(zL, KnotenL); schreibeKnoten(zR, KnotenR);
schreibeKnoten(w, Knoten);
return erfolgreich;
}
}
// Mische; weder links noch rechts kann etwas geborgt werden.
pivot = (i == n ? i - 1 : i);
zL = z[pivot]; zR = z[pivot+1];
// Fuege s[pivot] und *zR zu *zL:
leseKnoten(zL, KnotenL); leseKnoten(zR, KnotenR);
KnotenL.s[KnotenL.n] = s[pivot];
KnotenL.z[KnotenL.n + 1] = KnotenR.z[0];
for (j=0; j < KnotenR.n; j++)
{ KnotenL.s[KnotenL.n + 1 + j] = KnotenR.s[j];
KnotenL.z[KnotenL.n + 2 + j] = KnotenR.z[j+1];
}
KnotenL.n += 1 + KnotenR.n;
freierKnoten(zR);
for (j=i+1; j < n; j++)
{ s[j-1] = s[j]; z[j] = z[j+1];
}
Knoten.n--;
schreibeKnoten(zL, KnotenL); schreibeKnoten(w, Knoten);
return
Knoten.n >= (w == wurzel ? 1 : nMin) ? erfolgreich : Unterlauf;
}
void BBaum::leseKnoten(long w, knoten &Knoten)
{ if (w == NIL) return;
if (w == wurzel && wurzelKnoten.n > 0) Knoten = wurzelKnoten; else
{ datei.seekg(w, ios::beg);
datei.read((char*)&Knoten, sizeof(knoten));
}
}
void BBaum::schreibeKnoten(long w, const knoten &Knoten)
{ if (w == wurzel) wurzelKnoten = Knoten;
datei.seekp(w, ios::beg);
datei.write((char*)&Knoten, sizeof(knoten));
}
void BBaum::leseStart()
{ long start[2];
datei.seekg(0L, ios::beg);
datei.read((char *)start, 2 * sizeof(long));
wurzel = start[0]; freieListe = start[1];
12
Algorithmen und Datenstrukturen
leseKnoten(wurzel, wurzelKnoten);
}
long BBaum::holeKnoten()
{ long w;
knoten Knoten;
if (freieListe == NIL)
{ datei.seekp(0L, ios::end);
w = datei.tellp();
schreibeKnoten(w, Knoten);
// Belege Platz auf der Platte
} else
{ w = freieListe;
leseKnoten(w, Knoten);
// Zur Aktualisierung von freieListe:
freieListe = Knoten.z[0]; // Reduziere die freie Liste um 1
}
return w;
}
void BBaum::freierKnoten(long w)
{ knoten Knoten;
leseKnoten(w, Knoten);
Knoten.z[0] = freieListe;
freieListe = w;
schreibeKnoten(w, Knoten);
}
void main(void)
{ cout <<
"Demonstrationsprogramm eines Bayer-Baums auf einer Platte.
Die\n"
"Struktur des Bayer-Baums wird ueber die Schluessel
angezeigt.\n"
"Fuer jeden Knoten ist die Anzahl der verkettungen zu anderen
Knoten \n"
"nicht groesser als " << MAERER << "\n" <<
"Die Bayer-baum Darstellung ist aehnlich einem
Inhaltsverzeichnis \n"
"von einem Buch. Die in jedem Knoten gespeicherten Schluessel
\n"
"werden in einer Zeile ausgegeben.\n\n";
char BaumDateiname[50];
cout <<
"Gib den Namen einer (moeglicherweise nicht existierenden)\n"
"Binaerdatei fuer den Bayer-Baum an: ";
cin >> setw(50) >> BaumDateiname;
BBaum b(BaumDateiname);
cout <<
"\nGib eine (moeghlicherweise leere) Folge von Ganzzahlen,\n"
"gefolgt von einem Querstrich (/) an:\n";
dtype x;
char zeichen = 0;
while (cin >> x, !cin.fail())
{ b.einfuegen(x); zeichen = 1;
}
if (zeichen) b.gibAus();
cin.clear(); cin >> zeichen; // Ueberspringe terminierendes Zeichen
cout << "\nSollen Daten aus einer Textdatei gelesen "
"werden? (J/N): ";
cin >> zeichen;
if (toupper(zeichen) == 'J')
{ char eingabeDateiname[50];
cout << "Name dieser Textdatei: ";
cin >> setw(50) >> eingabeDateiname;
13
Algorithmen und Datenstrukturen
b.einfuegen(eingabeDateiname);
b.gibAus();
}
for (;;)
{ cout <<
"\nGib eine Ganzzahl ein, gefolgt von E, L, oder S (for\n"
"Einfuegen, Loeschen und Suche), oder gib Q zum Beenden ein: ";
cin >> x >> zeichen;
if (cin.fail()) break;
zeichen = toupper(zeichen);
switch(zeichen)
{
case 'S': b.zeigeSuche(x); break;
case 'E': b.einfuegen(x); break;
case 'L': b.loeschen(x); break;
default:
cout << "Falsches Kommando, benutze S, E oder L\n";
break;
}
if (zeichen == 'E' || zeichen == 'L') b.gibAus();
}
// return 0;
}
14
Herunterladen