Algorithmen und Datenstrukturen 11. Vorlesung

Werbung
B-Bäume – 1
B-Bäume (Bayer und McCreight, 1970) sind balancierte
Suchbäume, die für das effiziente Arbeiten mit Magnetbändern
oder anderen externen Speichern entwickelt wurden.
Algorithmen und Datenstrukturen
11. Vorlesung
Anders als Rot-Schwarz-Bäume (spez. binäre Suchbäume: ev.
später) minimieren B-Bäume den Zugriff (Lesen/Schreiben)
auf Magnetbänder. Viele Datenbanksysteme verwenden daher
B-Bäume zum Speichern und Verwalten sehr großer Informationsmengen, die nicht in den Hauptspeicher passen.
Karl-Heinz Niggl
13. Juni 2006
Der Verzweigungsgrad bei B-Bäumen (die Anzahl der Kinder
eines Knotens) kann sehr groß sein (oft rund 1000) und variiert,
bis auf die Wurzel, zwischen N −1 und 2N −1 Knoten, für
eine Konstante N ≥ 2, die Ordnung des B-Baums.
FG KTuEA, TU Ilmenau
AuD – 13.6.2006
FG KTuEA, TU Ilmenau
AuD – 13.6.2006
B-Bäume – 2
B-Bäume – 3
=⇒ Gegenüber Binärbäumen haben B-Bäume eine sehr
geringe Tiefe.
Bsp. B-Baum der Ordnung N = 3. Die Schüssel sind lateinische Großbuchstaben und wie üblich alphabetisch geordnet.
=⇒ B-Bäume dienen hervorragend zur Implementierung
von dynamischen Mengen: jede DynSet-Operation auf einem
B-Baum mit n Knoten benötigt nur Zeit O(log n).
Jeder innere Knoten x ungleich der Wurzel ist mit
Ein innerer Knoten x mit n[x] Schlüsseln hat n[x]+1 Kinder.
Nur die Wurzel darf auch weniger als N −1 Schlüssel besitzen.
Alle Blätter liegen in der gleichen Schicht.
root[T ]
N −1 ≤ n[x] ≤ 2N −1 Schlüssel s1 ≤ . . . ≤ sx[n]
M
aus einer totalen Ordnung (U, <) beschriftet und besitzt
n[x]+1 Kinder.
D H
Diese Schlüssel fungieren als Unterteilungspunkte der von x
verwalteten Schlüssel in n[x]+1 Teilbereiche (-bäume).
FG KTuEA, TU Ilmenau
AuD – 13.6.2006
1
2
B C
F
FG KTuEA, TU Ilmenau
G
Q T
J
K L
AuD – 13.6.2006
N P
R S
X
V
W
Y
Z
3
B-Bäume – 4
B-Bäume – 5
Frage: Warum werden Datenstrukturen mit Zugriff auf Magnetbänder anders bewertet als Datenstrukuren mit wahlfreiem
(random-access) Hauptspeicherzugriff?
Grundsätzlich:
Der verfügbare Speicherplatz in einem Computer zerfällt grob
in einen Hauptspeicher (üblicherweise aus einem Silikon-Chip
bestehtend) und externe Speicher, kurz Disks (üblicherweise
basierend auf Magnetbändern).
Die Kosten für das Speichern eines Bits auf einem Silikon-Chip
sind i.a. etwa doppelt so hoch wie bei Magnetbändern.
Aber der externe Speicher ist i.a. mindestens doppelt so groß
wie der Hauptspeicher.
FG KTuEA, TU Ilmenau
AuD – 13.6.2006
4
Ein typisches Laufwerk besteht aus verschiedenen Platten
mit magnetisierbarer Oberfläche, die um eine gemeinsame
Spindel mit konstanter Geschwindigkeit rotieren.
Jede Platte besitzt einen Lese-/Schreibkopf und ist in sog.
Spuren (Tracks) eingeteilt. Die Lese-/Schreibköpfe sind nur
synchron innerhalb eines Zylinders bewegbar.
Ein Zylinder ist dabei die Menge der übereinanderliegenden
gleichen Spuren.
=⇒ Der Zugriff auf externe Speicher ist viel langsamer,
da mechanische Bewegung involviert ist: Rotation der Platten
und Bewegung der Lese-/Schreibköpfe.
FG KTuEA, TU Ilmenau
AuD – 13.6.2006
5
B-Bäume – 6
B-Bäume – 7
Möglicherweise veraltet: Handelsübliche Disks vollführen 5400
bis 15000 Umdrehungen pro Minute.
Um diesen Nachteil zu minimieren, wird Information in gleichgroßen Seiten (pages) auf mehreren Platten innerhalb eines
b Lesen oder Schreiben
Zylinders organisiert. 1 Diskzugriff =
einer oder mehrerer Seiten. 1 Seite =
b 211 bis 214 Bytes.
Eine Umdrehung benötigt rund 8 Millisekunden. Das ist in etwa
das Fünffache der üblichen Zugriffszeit (100 Nanosekunden)
bei Silikonchips.
Wenn man eine volle Umdrehung warten muß, damit der Lese/Schreibkopf einen bestimmten Spureintrag lesen kann, so
kann man in dieser Zeitspanne beinahe 100000 Zugriffe auf
den Hauptspeicher ausführen.
Das Bewegen der Lese-/Schreibköpfe auf die angefragte Spur
kostet 3–9 Millisekunden.
FG KTuEA, TU Ilmenau
AuD – 13.6.2006
6
Oft wird mehr Zeit für den Zugriff der Informationen einer
Seite benötigt als diese dann im Hauptspeicher zu verarbeiten.
Daher: Bei der Analyse der folgenden Implementierung von
Wörterbuch-Operationen werden zwei Bestandteile der Laufzeit separt betrachtet:
• Anzahl der Disk-Zugriffe
• CPU-Rechenzeit
FG KTuEA, TU Ilmenau
AuD – 13.6.2006
7
B-Bäume – 8
B-Bäume – 9
Die Anzahl der Disk-Zugriffe =
b Anzahl der gelesenen oder
zurückgeschriebenen Seiten. Der Diskzugriff selbst ist keine
Konstante, da er vom Abstand der zuletzt gelesenen und der
angefragten Spur abhängt.
Neben den üblichen Komponenten der Objekte x (Zeiger auf
Knoten) in einem B-Baum existieren in unserem Pseudocode
die folgenden Operationen:
• disk-read(x): Liest das auf der Disk gespeicherte Objekt,
auf das x zeigt, und holt es in den Hauptspeicher (erst dann
kann auf die Komponenten von x zugegriffen werden!).
Ein B-Baum speichert i.a. dramatisch viel mehr Informationen
als der Hauptspeicher fassen kann. Daher werden von BBaum-Algorithmen nur soviel Seiten aus den Disks ausgelesen,
in den Hauptspeicher geholt und bei Veränderung wieder
zurückgeschrieben wie notwendig sind.
Die folgenden Algorithmen sind so gestaltet, daß man zu jeden
Zeitpunkt nur O(1) Seiten im Hauptspeicher benötigt.
FG KTuEA, TU Ilmenau
AuD – 13.6.2006
8
• disk-write(x): Schreibt das im Hauptspeicher befindliche
Objekt, auf das x zeigt, auf die Disk zurück (nur anwenden,
wenn sich x verändert hat!).
Nun ist klar: Die Operationen disk-read und disk-write
müssen möglichst effizient eingesetzt werden!
FG KTuEA, TU Ilmenau
AuD – 13.6.2006
9
B-Bäume – 10
B-Bäume – 11
Daher ist üblicherweise ein B-Baum-Knoten so groß wie eine
Disk-Page und die Anzahl der Kinder eines Knotens ist daher
auch durch die Größe einer Page beschränkt.
Def. Ein B-Baum der Ordnung N , N ≥ 2, ist ein Baum T
mit Wurzel root[T ] und den folgenden Eigenschaften:
Bei einem großen B-Baum bewegt sich der Verzweigungsgrad
üblicherweise zwischen 50 und 2000. Ein hoher Verzweigungsgrad reduziert drastisch sowohl die Höhe eines B-Baumes
als auch die Anzahl der Disk-Zugriffe, um einen Schüssel zu
finden.
Bsp. Ein B-Baum der Höhe 2 mit Verzweigungsfaktor 1001,
kann über einer Milliarde Schlüssel besitzen. Da die Wurzel
stets im Hauptspeicher gehalten wird, braucht man maximal
zwei Disk-Zugriffe, um einen Schlüssel zu finden.
FG KTuEA, TU Ilmenau
AuD – 13.6.2006
10
1. Jeder Knoten x (Zeiger auf) besitzt folgende Komponenten:
n[x]: verwaltet die Anzahl der Schlüssel in x; für diese gilt:
key1[x] ≤ . . . ≤ keyn[x][x]
leaf[x]: Boolescher Wert mit leaf[x] = true ⇐⇒ x ist Blatt.
2. Jeder innere Knoten x besitzt n[x]+1 (Zeiger auf) Kinder
c1[x], . . . , cn[x]+1[x].
Bei einem Blatt sind die Komponenten ci undefiniert (NIL).
FG KTuEA, TU Ilmenau
AuD – 13.6.2006
11
B-Bäume – 12
B-Bäume – 13
3. Jeder Schüssel keyi[x] besitzt einen linken Teilbaum Li[x]
mit Wurzel ci[x] und einen rechten Teilbaum Ri[x] mit
Wurzel ci+1[x] und es gilt folgende B-Baum-Eigenschaft:
Bem. In einem B-Baum der Ordnung N besitzt jeder innere
Knoten (außer der Wurzel) ≥ N und ≤ 2N Kinder.
keys(Li[x]) ≤ keyi[x] ≤ keys(Ri[x])
für i = 1, . . . , n[x].
4. Die Blätter von T liegen auf Schicht h(T ) := Höhe(T ).
Bem. B-Bäume der Ordnung N ≥ 1 werden auch so definiert,
daß jeder innere Knoten (außer der Wurzel) ≥ N und ≤ 2N
Schlüssel und damit ≥ N +1 und ≤ 2N +1 Kinder besitzt.
5. Für jeden inneren Knoten x 6= root[T ] gilt:
In einem solchen B-Baum der Ordnung N = 1 besitzt jeder
innere Knoten (außer der Wurzel) zwei oder drei Kinder. Man
spricht hier von einem 2-3-Baum.
N −1 ≤ n[x] ≤ 2N −1
Für x = root[T ] mit T 6= NIL gilt: 1 ≤ n[x] ≤ 2N −1
FG KTuEA, TU Ilmenau
AuD – 13.6.2006
In einem B-Baum der Ordnung N = 2 besitzt also jeder innere
Knoten, mit Ausnahme der Wurzel, zwei, drei oder vier Kinder.
Man spricht hier von einem 2-3-4-Baum.
12
FG KTuEA, TU Ilmenau
AuD – 13.6.2006
13
B-Bäume – 14
B-Bäume – 15
Satz (Höhe v. B-Bäumen). Sei T ein B-Baum der Ordnung
N mit n Schlüsseln. Dann gilt: h(T ) ≤ logN n+1
2
Konventionen für die folgenden Operationen b-tree-search,
b-tree-create, b-tree-insert, b-tree-delete:
• root[T ] befindet sich stets im Hauptspeicher.
disk-read(root[T ]) ist damit überflüssig;
disk-write(root[T ]) ist nur erforderlich, falls sich die Wurzel verändert hat.
Beweis. Sei h := h(T ). Nach Def. gilt: root[T ] besitzt ≥ 1
und jeder andere innere Knoten ≥ N −1 Schlüssel.
=⇒ Schicht 1 besitzt mindestens 2·N 0 Knoten.
Schicht 2 besitzt mindestens 2·N 1 Knoten.
Schicht 3 besitzt mindestens 2·N 2 Knoten . . .
Schicht h besitzt mindestens 2·N h−1 Knoten.
h
X
2·N i−1
=⇒ n ≥ 1 + (N −1)·
i=1
= 1 + 2·(N −1)·
FG KTuEA, TU Ilmenau
AuD – 13.6.2006
Nh − 1
= 2·N h − 1
N −1
• Für jeden Knoten (Zeiger) x, der als Argument an eine Prozedur übergebenen wird, muß vorher einmal disk-read(x)
erfolgt sein.
Beachte: Alle Operationen werden so implementiert, daß man
sich stets von der Wurzel in Richtung eines Blattes bewegt.
14
FG KTuEA, TU Ilmenau
AuD – 13.6.2006
15
B-Bäume – 16
B-Bäume – 17
b-tree-search(x, k) liefert einen Zeiger y auf einen Knoten
sowie einen Index i mit keyi[y] = k, falls sich Schlüssel k im
Teilbaum mit Wurzel x befindet, und NIL sonst.
1:
2:
3:
4:
5:
6:
7:
8:
AuD – 13.6.2006
1.Fall: i ≤ n[x] und k = keyi[x]. OK!
2.Fall: x ist ein Blatt. OK!
3.Fall a): i = n[x] + 1
=⇒ k > keyn[x][x] und k kann sich höchstens im rechten
Teilbaum Rn[x][x] mit Wurzel ci[x] befinden. OK!
procedure b-tree-search(x, k)
i←1
while (i ≤ n[x] ∧ k > keyi[x] do
i←i+1
if (i ≤ n[x] ∧ k = keyi[x]) then return (x, i)
if leaf[x] then return NIL
else disk-read(ci[x])
b-tree-search(ci[x], k)
FG KTuEA, TU Ilmenau
Korrektheit: Nach Abbruch der while-Schleife.
3.Fall b): i ≤ n[x] und k ≤ keyi[x]
=⇒ k kann sich höchstens im linken Teilbaum Li[x] mit
Wurzel ci[x] befinden. OK!
Laufzeit: Die Anzahl der Disk-Zugriffe ist O(h) = O(logN n);
die CPU-Zeit beträgt O(N ·h) = O(N ·logN n).
16
FG KTuEA, TU Ilmenau
AuD – 13.6.2006
17
B-Bäume – 18
B-Bäume – 19
b-tree-create(T ) liefert einen Zeiger T auf die Wurzel eines
leeren B-Baums in CPU-Zeit O(1) und O(1) Disk-Zugriffen.
Einfügen eines Schlüssels in B-Baum: Sei k ein Schlüssel
und T ein B-Baum der Ordnung N . Wie bei Binärbäumen suchen wir zunächst ein geeignetes Blatt, um darin x einzufügen.
Hilfsprozedur allocate-node() allokiert auf der Disk eine
Page für einen neuen Knoten. allocate-node() benötigt
keinen Aufruf disk-read.
1:
2:
3:
4:
5:
6:
procedure b-tree-create(T )
x ← allocate-node()
leaf[x] ← true
n[x] ← 0
disk-write(x)
root[T ] ← x
FG KTuEA, TU Ilmenau
AuD – 13.6.2006
18
Aber: Anders als bei Binärbäumen können wir nicht einfach
ein neues Blatt erzeugen und dort k einfügen. Bedingung 4.
als auch Bedingung ≥ N −1 Schlüssel“ wäre i.a. verletzt.
”
Stattdessen fügen wir k in ein existierendes Blatt x ein. Falls x
voll ist, verletzten wir dabei Bedingung ≤ 2N −1 Schlüssel“.
”
Naive Lösung: Wir spalten x um Medianschlüssel keyN [x]
in zwei neue Blätter mit N −1 Schlüssel; keyN [x] wandert als
neuer Unterteilungspunkt in den Vaterknoten y von x.
FG KTuEA, TU Ilmenau
AuD – 13.6.2006
19
B-Bäume – 20
B-Bäume – 21
Problem: Nun könnte ein Overflow in y vorliegen. Also müsste
man y ebenfalls aufspalten, u.s.w. Schlimmstenfalls würde die
Behandlung eines entstehenden Overflows bis hoch zur Wurzel
propagiert werden.
Dies kann doppelt so viele Disk-Zugriffe als nötig verursachen!
Clevere Lösung: Innerhalb des Suchpfades in T nach einem
Einfügeblatt für x wird jeder volle Knoten aufgespalten.
Bsp. Splitting eines Knoten in B-Baum der Ordnung N = 4.
AuD – 13.6.2006
20
x
··· D W ···
··· D S
y = ci [x]
P
U V
7→
P
T
Q R
U V
T5 T6 T7 T8
T1 T2 T3 T4
root[T ]
s
H
root[T ]
r
A D F
H L
N P
T1 T2 T3 T4 T5 T6 T7 T8
FG KTuEA, TU Ilmenau
7→
r
A D F
L N P
T1 T2 T3 T4
T5 T6 T7 T8
AuD – 13.6.2006
21
B-Bäume – 23
b-tree-split-child(x, i, y) erwartet als Input:
• einen nicht vollen inneren Knoten x (im Hauptspeicher)
• einen Index i
• einen vollen Knoten y (im Hauptspeicher) mit y = ci[x].
Die Prozedur spaltet y in zwei Knoten y und z mit jeweils N−1
Schlüssel; der Medianschlüssel keyN [x] wird neuer Schüssel
von x mit linkem Teilbaum Ty und rechtem Teilbaum Tz .
Spalten der Wurzel: root[T ] wird zunächst zum einzigen
Kind eines neuen, leeren Wurzelknotens s. root[T ] wird nun
durch Aufruf b-tree-split-child(s, 1, root[T ]) gespalten.
Dies ist die einzige Möglichkeit, h(T ) zu vergößern!
AuD – 13.6.2006
T
Bsp. Splitting der Wurzel eines B-Baums der Ordnung N = 4.
B-Bäume – 22
FG KTuEA, TU Ilmenau
W ···
y = ci [x]
Q R S
T1 T2 T3 T4 T5 T6 T7 T8
Wirkung: Keine Split-Operation erzeugt einen Overflow! Die
Einfüge-Operation erfolgt in einer Bewegung von der Wurzel
in Richtung eines Blattes. Ein Rücklauf (back up) ist nicht
erforderlich.
FG KTuEA, TU Ilmenau
x
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
procedure b-tree-split-child(x, i, y)
z ← allocate-node()
leaf[z] ← leaf[y]
n[z] ← N −1
for j ← 1 to N −1 do keyj [z] ← keyj+N [y]
if not leaf[y] then
for j ← 1 to N do cj [z] ← cj+N [y]
n[y] ← N −1
for j ← n[x] + 1 downto i + 1 do cj+1[x] ← cj [x]
ci+1[x] ← z
for j ← n[x] downto i do keyj+1[x] ← keyj [x]
keyi[x] ← keyN [y]
disk-write(y); disk-write(z); disk-write(x)
Laufzeit: CPU-Zeit O(N ) und O(1) Disk-Zugriffe
22
FG KTuEA, TU Ilmenau
AuD – 13.6.2006
23
B-Bäume – 24
B-Bäume – 25
b-tree-insert(T, k) fügt Schlüssel k in den B-Baum T ein.
Bsp. Einfügen in einen B-Baum der Ordnung N = 3.
G M P
Der Fall einer vollen Wurzel wird separat behandelt: Für nicht
volle Knoten ist b-tree-insert-nonfull zuständig.
procedure b-tree-insert(T, k)
r ← root[T ]
if n[r] = 2N −1 then
s ← allocate-node()
root[T ] ← s
leaf[s] ← false; n[s] ← 0
c1[s] ← r
b-tree-split-child(s, 1, r)
b-tree-insert-nonfull(s, k)
10:
else b-tree-insert-nonfull(r, k)
1:
2:
3:
4:
5:
6:
7:
8:
9:
FG KTuEA, TU Ilmenau
A C D E
A B C D E
A B C D E
FG KTuEA, TU Ilmenau
B-Bäume – 26
P
B C D E
J
K L
Einfügen von F:
T
N O
Q R S
A B
FG KTuEA, TU Ilmenau
X
U
V
Y
Z
D E F
J
AuD – 13.6.2006
K L
T
N O
Q R S
J
K
N O
J
K
N O
U V
Y
Z
X
R S
G M P
T
T
T
U
V
Y
Z
U
V
Y
Z
X
Q R S
AuD – 13.6.2006
25
V
Y
Z
X
U
1:
2:
3:
4:
5:
procedure b-tree-insert-nonfull(x, k)
i ← n[x]
if leaf[x] then
⊲ Füge k in Blatt x ein.
while i ≥ 1 und k < keyi[x] do
⊲ Korrekt, da x nicht voll!
keyi+1[x] ← keyi[x]; i ← i−1
6:
7:
keyi+1[x] ← k
n[x] ← n[x]+1; disk-write(x)
else while i ≥ 1 und k < keyi[x] do
i ← i−1
⊲ Suche nach geeignetem Teilbaum
i ← i+1
⊲ mit Wurzel ci[x].
disk-read(ci[x])
if n[ci[x]] = 2N −1 then
b-tree-split-child(x, i, ci[x])
if k > keyi[x] then i ← i+1
b-tree-insert-nonfull(ci[x], k)
8:
9:
10:
11:
12:
13:
14:
15:
P
C G M
R S
B-Bäume – 27
Bsp. fortgesetzt
A
N O
G M P
Einfügen von Q:
24
G M
K
Einfügen von B:
AuD – 13.6.2006
Einfügen von L:
J
X
26
FG KTuEA, TU Ilmenau
AuD – 13.6.2006
27
B-Bäume – 28
B-Bäume – 29
Laufzeit: Die Anzahl der Disk-Zugriffe ist O(h) = O(logN n);
die CPU-Zeit beträgt O(N ·h) = O(N ·logN n).
Problem: Dadurch könnte nun in y ein Underflow vorliegen.
Also müsste man auch y schlimmstenfalls über seinen Vater
ausgleichen, u.s.w. Schlimmstenfalls würde die Behandlung
eines Underflows bis hoch zur Wurzel propagiert werden.
Löschen eines Schlüssels in einem B-Baum: Komplizierter,
da zu löschender Schlüssel in einem beliebigen Knoten sein
kann. Sei k ein Schlüssel und T ein B-Baum der Ordnung N .
Dies kann doppelt so viele Disk-Zugriffe als nötig verursachen!
Analog zum Einfügen muß nun beim Löschen von k aus
einem Knoten x 6= root[T ] die Bedingung ≥ N −1 Schlüssel“
”
sichergestellt werden.
Clevere Lösung: b-tree-delete(x, k) löscht Schlüssel k
aus Teilbaum mit Wurzel x derart, daß vor jedem (rekursiven)
Aufruf n[x] ≥ N gilt.
Naive Lösung: Tritt nach dem Löschen von k ein Underflow
auf, so holt man sich schlimmstenfalls einen Schlüssel aus dem
Vaterknoten y und gleicht aus.
Wirkung: b-tree-delete erzeugt keinen Underfow und erfolgt (ohne Rücklauf) in einer Bewegung von der Wurzel in
Richtung eines Blattes.
FG KTuEA, TU Ilmenau
AuD – 13.6.2006
28
FG KTuEA, TU Ilmenau
AuD – 13.6.2006
B-Bäume – 30
B-Bäume – 31
Arbeitsweise von b-tree-delete(x, k): (Ohne Pseudocode)
1.Fall: leaf[x], k ∈ keys(x) und n[x] ≥ N
Bsp. Fall 2a) im Bild:
i
=⇒ Lösche k aus x.
··· k
Für x = root[T ], k = key1[x] und n[x] = 1 ist T danach leer.
a) Für den linken Teilbaum Li[x] von k mit Wurzel
y := ci[x] gilt n[y] ≥ N .
···
x
30
· · · k′
···
x
y = ci [x]
k′
Bestimme den Predecessor k ′ von k (= max keys(Li[x])).
Lösche rekursiv k ′ aus Li[x] und ersetze k durch k ′.
(Dies erfordert eine eigene, analoge Delete-Prozedur!)
AuD – 13.6.2006
i
y = ci [x]
2.Fall: ¬leaf[x], k = keyi(x) und n[x] ≥ N .
FG KTuEA, TU Ilmenau
29
FG KTuEA, TU Ilmenau
AuD – 13.6.2006
31
B-Bäume – 32
B-Bäume – 33
b) Für den rechten Teilbaum Ri[x] von k mit Wurzel
z := ci+1[x] gilt n[z] ≥ N .
c) k besitzt einen linken Teilbaum mit Wurzel y und einen
rechten Teilbaum mit Wurzel z und n[y] = N −1 = n[z].
Bestimme den Successor k ∗ von k (= min keys(Ri[x])).
Lösche rekursiv k ∗ aus Ri[x] und ersetze k durch k ∗.
(Dies erfordert eine eigene, analoge Delete-Prozedur!)
i
i
··· k ··· x
· · · k∗ · · · x
z = ci+1 [x]
z = ci+1 [x]
Verschmelze y und z zu einem vollen Knoten y mit
Medianschlüssel k (z wird dann freigegeben).
Dann erfolgt Aufruf b-tree-delete(y, k).
i
y
· · · k− k k+ · · ·
x
···
z
···
R
k∗
FG KTuEA, TU Ilmenau
AuD – 13.6.2006
32
FG KTuEA, TU Ilmenau
B-Bäume – 34
Verschmelze y und z zu einem vollen, neuen Wurzelknoten
y mit Medianschlüssel k (z wird dann freigegeben).
Dann erfolgt Aufruf b-tree-delete(y, k).
y
x
···
R
FG KTuEA, TU Ilmenau
L
AuD – 13.6.2006
z
L
R
z
L
AuD – 13.6.2006
33
k
y
R
3.Fall: ¬leaf[x], k ∈
/ keys[x] und n[x] ≥ N .
Suche Wurzel ci[x] desjenigen Teilbaums Ti, der k enthalten
muß (wenn k überhaupt im Baum sein soll).
a) n[ci[x]] ≥ N . =⇒ Aufruf b-tree-delete(ci[x], k).
b) n[ci[x]] = N −1 und Ti besitzt einen rechten Teilbaum
mit Wurzel ci+1[x] und n[ci+1[x]] ≥ N .
root[T ]
···
k
y
x
B-Bäume – 35
d) x = root[T ], k = key1[x], n[x] = 1 und n[y] = N −1 = n[z]
k
· · · k− k+ · · ·
=⇒ Schlüssel ki := keyi[x] wandert in ci[x] hinab als
letzter Schlüssel mit aus ci+1[x] geborgtem rechten
Teilbaum L des ersten Schlüssels s aus ci+1[x].
Schlüssel s wandert nach oben in x anstelle von ki.
Dann erfolgt Aufruf b-tree-delete(ci[x], k).
z
L
34
FG KTuEA, TU Ilmenau
AuD – 13.6.2006
35
B-Bäume – 36
i
x ···
ki · · ·
B-Bäume – 37
i
···
s ···
x
i
x ···
Ti
s s+ · · ·
···
···
ki
s+
i
ki · · ·
L
L
L
x
···
···
Ti
s−
···
ki
+
L
R− R
c) n[ci[x]] = N −1 und Ti besitzt einen linken Teilbaum
mit Wurzel ci−1[x] und n[ci−1[x]] ≥ N .
AuD – 13.6.2006
R− R
d) n[ci[x]] = N −1 und Ti besitzt linken Teilbaum L mit
Wurzel y := ci−1[x] und rechten Teilbaum R mit
Wurzel z := ci+1[x] und n[y] = N −1 = n[z]
=⇒ Schlüssel ki := keyi[x] wandert in ci[x] hinab als
erster Schlüssel mit aus ci−1[x] geborgtem linken
Teilbaum R des letzten Schlüssels s aus ci−1[x].
Schlüssel s wandert nach oben in x anstelle von ki.
Dann erfolgt Aufruf b-tree-delete(ci[x], k).
FG KTuEA, TU Ilmenau
s ···
···
· · · s− s
+
···
Verschmelze ci[x] und z zu einem vollen Knoten ci[x]
mit Medianschlüssel ki (z wird dann freigegeben).
Dann erfolgt Aufruf b-tree-delete(ci[x], k).
36
FG KTuEA, TU Ilmenau
AuD – 13.6.2006
B-Bäume – 38
37
B-Bäume – 39
Bsp. Löschen in einem B-Baum der Ordnung N = 3.
Bsp. Fall 3d) im Bild:
P
i
x
C G M
· · · ki− ki ki− · · ·
x
A B
L
ci [x]
R
L
T
ci [x] ki
D E F
J
K L
N O
Q R S
V
Y
Z
P
C G M
Laufzeit: Die Anzahl der Disk-Zugriffe ist O(h) = O(logN n);
die CPU-Zeit beträgt O(N ·h) = O(N ·logN n).
AuD – 13.6.2006
U
R
Löschen von F: Fall 1
FG KTuEA, TU Ilmenau
X
· · · ki− ki− · · ·
38
A B
FG KTuEA, TU Ilmenau
D E
J
K L
AuD – 13.6.2006
T
N O
Q R S
X
U V
Y
Z
39
B-Bäume – 40
B-Bäume – 41
Löschen von D: Fall 3b)
Bsp. fortgesetzt
C L P
Löschen von M: Fall 2a)
A B
C G L
T
D E
J K N O
Q R S
U
V
Y
E J K
P
FG KTuEA, TU Ilmenau
D E J K
AuD – 13.6.2006
T
N O
Q R S
U V
Y
Z
E J K
N O
T
X
Q R S
U V
Y
Z
Löschen von B: Fall 3a)
C L
A B
N O
C L P
Z
A B
Löschen von G: Fall 2c)
X
X
Baum schrumpft
A B
T
P
Q R S
E L P
X
U
V
Y
Z
A C
40
FG KTuEA, TU Ilmenau
J K
AuD – 13.6.2006
N O
T
X
Q R S
U V
Y
Z
41
Herunterladen