flattern

Werbung
7.2 Suchbäume
= Bäume zur Repräsentation linear geordneter Mengen
Binäre Bäume:
• unbalanciert
• balanciert:
AVL-Baum
Rot-Schwarz-Baum
Vielweg-Suchbäume:
• 2-3-Baum
• B-Baum
• B*-Baum
....
alp3-7.2
1
7.2.1 Binäre Suchbäume
Beispiel: { 1, 3, 4, 5, 7, 9, 11 } kann dargestellt werden als
Abstraktionsfunktion
5
3
1
Invariante:
7
4
11
9
alp3-7.2
Die Wurzel ist größer als alle
Elemente im linken Teilbaum
und kleiner als alle Elemente
im rechten Teilbaum, und
die Teilbäume sind ebenfalls
binäre Suchbäume.
2
Suchen:
Iterativ von der Wurzel nach unten, wobei an
jedem Scheideweg die Entscheidung aufgrund
der Suchbaum-Eigenschaft getroffen wird.
Einfügen:
ebenso, mit Anhängen eines neuen Blatts
(falls Element noch nicht vorhanden)
Achtung:
Auch hier ist die Abstraktionsfunktion nicht injektiv:
ein und dieselbe Menge kann durch verschiedene
Bäume dargestellt werden:
MO
DI
SA
MI
DO
alp3-7.2
MI
DO
SO
DI
FR
SA
FR
MO
SO
3
7.2.1.1 Suchbaum funktional
module TreeSets(TreeSet,
contains, add, remove, card, traverse)
where
data Ord t => TreeSet t = E | N (TreeSet t) t (TreeSet t)
abs E = {}
-- pseudo-Haskell
abs(N l x r) = abs l ++ {x} ++ abs r
inv E = True
inv(N l x r) = all(<x)(abs l) && all(>x)(abs r) &&
inv l && inv r
contains ...
alp3-7.2
4
contains E x = False
contains(N l y r) x
| x<y = contains l x
| x==y = True
| x>y = contains r x
add x E = N E x
add x(N l y r)
| x<y =
| x==y =
| x>y =
E
N (add x l) y r
N l y r
N l y (add x r)
card E = 0
card(N l _ r) = 1 + card l + card r
-- cf. 6.2.1
flatten E = []
-- inorder
-- cf. 6.4.1
flatten(N l x r) = flatten l ++ [x] ++ flatten r
-- traversieren, liefert Elemente in sortierter Reihenfolge!
alp3-7.2
5
remove ... ?
9
2
2
4
4
8
8
6
5
alp3-7.2
6
5
7
7
(falls nur rechter Teilbaum: entsprechend)
6
remove ... ?
9
2
12
4
8
6
5
alp3-7.2
7
7
remove ... ?
(oder spiegelbildlich)
2
9
2
12
4
8
4
6
8
5
6
5
alp3-7.2
7
12
7
Nachteil: drohende
Unausgeglichenheit
8
remove ... ?
9
2
12
4
8
6
5
alp3-7.2
7
9
remove ... ?
9
2
8
12
4
2
12
4
8
6
5
alp3-7.2
6
7
5
7
(oder spiegelbildlich)
10
remove x E = E
remove
|
|
-|
|
|
alp3-7.2
x(N l y r)
x<y
= N(remove x l) y r
x>y
= N l y(remove x r)
x==y
l==E = r
r==E = l
otherwise = N l' (max l) r
where l' = remove(max l)l
max(N l x E) = x
max(N l x r) = max r
11
7.2.1.2 Suchbaum imperativ
Effiziente Implementierung des Suchbaums typischerweise
unter Verzicht auf zusätzliche Datenabstraktion
(„abstrakte Menge als konkretes Geflecht dargestellt“) !
class TreeSet<T extends Comparable<T>> implements Set<T> {
protected class Node { T value; Node left, right;
Node(T v) { value = v; } }
protected Node root = null;
// inv ... *
// abs root = { x | x<- trav root}
//
where trav n = if n==null then []
//
else trav(n.l) ++ [n] ++ trav(n.r)
public boolean contains(T x) ...
public void add(T x) ...
.....
}
*Übung - Baumstruktur sicherstellen und Duplikate ausschließen!
alp3-7.2
12
public void add(T x) throws NullPointerException {
if(x==null) throw new NullPointerException();
Node n = new Node(x);
if(root==null) { root = n; return; }
Node current=root;
// iterative navigation
do{ int compare = current.value.compareTo(x);
if(compare==0) return; // x in set
if(compare>0)
if(current.left == null) {
current.left = n; return; }
else current = current.left;
else/*compare<0*/
if(current.right == null) {
current.right = n; return; }
else current = current.right;
}while(true);
}
alp3-7.2
13
public boolean contains(T x) ...
ähnlich, etwas einfacher
public void remove(T x) ...
schwieriger, vergleiche 7.2.1.1
public int card() ...
rekursiv, vergleiche 7.2.1.1 ( daher empfiehlt
sich, card als zusätzliches Attribut zu führen!)
public void traverse(Operation<T> op) ...
interner Iterator, rekursiv, vergleiche 6.4.1.1
public Iterator<T> iterator() ...
externer Iterator, vergleiche 6.4.3
Die Iteratoren beachten die Ordnung auf T.
alp3-7.2
14
7.2.1.3 Komplexität
Suchen, Einfügen, Löschen - alle Operationen verfolgen einen
Weg von der Wurzel nach unten (d.i. kein Traversieren!).
Die Länge eines solchen Wegs ist bei vollständigen Bäumen mit
n Knoten höchstens log2(n+1) - 1 (6.1.3.3). Der Aufwand
für die Operationen ist also bei vollständigen Bäumen O(log n)
- selbst im ungünstigsten Fall.
alp3-7.2
15
7.2.1.3 Komplexität
Suchen, Einfügen, Löschen - alle Operationen verfolgen einen
Weg von der Wurzel nach unten (d.i. kein Traversieren!).
Die Länge eines solchen Wegs ist bei vollständigen Bäumen mit
n Knoten höchstens log2(n+1) - 1 (6.1.3.3). Der Aufwand
für die Operationen ist also bei vollständigen Bäumen O(log n)
- selbst im ungünstigsten Fall.
Aber: je ungleichgewichtiger der Baum ausfällt, desto höher kann
er werden; im Extremfall ist die Höhe n - z.B. wenn beim Einfügen
der Werte in sortierter Reihenfolge ein Kette entsteht:
DI
DO
FR
alp3-7.2
MI
...
16
Aufwand für erfolglose Suche:
Baum ist
vollständig
Schritte
log2(n+1)
Komplexität
O(log n)
entartet: Kette
n
O(n)
zufällig
(Mittelwert für große n)
≈ 1,39 log2 n - 1,85
O(log n) !
(siehe z.B. Güting, 4.2)
alp3-7.2
17
7.2.2 Ausgeglichene Bäume
(balanced trees)
... sind „möglichst“ vollständig, mit garantierter Komplexität O(log n)
Def.:
Ein binärer Suchbaum heißt k-ausgeglichen (k ∈ N),
wenn für jeden Knoten die Höhen seiner beiden Teilbäume
um höchstens k voneinander abweichen.
alp3-7.2
18
7.2.2.1 AVL-Bäume
(Adelson-Velskij/Landis 1962)
Def.:
Ein AVL-Baum ist ein 1-ausgeglichener Suchbaum.
Bemerkung 1:
Ein vollständiger Baum (6.1.2) ist ein AVL-Baum.
Bemerkung 2: Ein AVL-Baum ist nicht notwendig vollständig, z.B.
+1
-1
+1
+1
Höhendifferenz:
Höhe linker Teilbaum
minus
Höhe rechter Teilbaum
Bemerkung 3: Ein AVL-Baum ist ein Kompromiss zwischen einem
beliebigen Baum und einem vollständigen Baum.
alp3-7.2
19
Abstraktionsfunktion: wie bei allen binären Suchbäumen
Invariante:
inv aus 7.2.1.1 wird zu avl erweitert
(mit h t = Höhe von t ) :
avl E = True
avl(N l x r) = inv(N l x r) &&
-- from 7.2.1.1
abs(h l - h r) <= 1 &&
avl l && avl r
 Aufrechterhaltung der Invariante:
1. Einfügen/Löschen wie bekannt ...
2. ... außerdem gegebenenfalls ausgleichen (rebalancing).
alp3-7.2
20
x
-2
-1
-1
 Wenn ein Knoten aus dem Gleichgewicht gerät, ist die Höhe
seines größeren Teilbaums um 1 gewachsen.
 Nur die Knoten auf dem Weg vom eingefügten Blatt zur Wurzel
können betroffen sein; der erste solche Knoten sei x.
alp3-7.2
21
x
-2
-1
-1
 Der x-Baum kann so rearrangiert werden, dass er wieder die
alte Höhe hat (denn er war nicht vollständig besetzt!).
 Alle Knoten von x bis zur Wurzel sind damit wieder balanciert
(denn sie waren es auch vorher).
alp3-7.2
 Somit genügt es, den x-Baum auszugleichen.
22
Fall 1:
x
A
<x
alp3-7.2
-2
y -1
B
>x
<y
C
>y
23
Fall 1:
x
A
<x
„Rotation“
-2
y
y -1
B
>x
<y
x
C
A
>y
<x
B
>x
<y
C
>y
... und spiegelbildlich ebenso
alp3-7.2
24
Fall 1:
x
A
<x
„Rotation“
-2
y
y +1
B
>x
<y
x
C
A
>y
<x
B
>x
<y
C
>y
... scheitert aber bei Erweiterung von B !
alp3-7.2
25
Fall 2a,b:
x
-2
+1
z +1
y
A
B1
alp3-7.2
B2
C
26
„Doppelrotation“
Fall 2a,b:
x
y
A
z
B1
x
B2
-2
+1
C
z +1
x
alp3-7.2
z
y -1
A
B1
y
B2
C
A
B1
... und spiegelbildlich ebenso
B2
C
27
Präzisierung mit Haskell (vgl. 7.2.1.1):
module TreeSets(TreeSet, add, remove, contains, ...)
where
data Ord t => TreeSet t = E | N (TreeSet t) t (TreeSet t)
contains .....
-- wie für einfache Suchbäume
l(N l _ r) = l
r(N l _ r) = r
bal E = 0
-- balance
bal(N l _ r) = h l - h r
add x E = N E x E
add x(N l y r) = if abs(bal new)<2 then new else rot new
where new | x<y = N(add x l) y r
| x==y = N l y r
| x>y = N l y(add x r)
alp3-7.2
28
rot t | bal t == -2 && bal(r t) == -1
where N a x(N b y c) = t
| bal t == 2 && bal(l t) == 1
where N(N c y b)x a = t
= N(N a x b) y c
-- Fall 1
= N c y(N b x a)
-- gespiegelt
| bal t == -2 && bal(r t) == 1
= N(N a x b1) y (N b2 z c)
where N a x(N(N b1 y b2) z c) = t -- Fall 2
| bal t == 2 && bal(l t) == -1
= N(N c z b2) y (N b1 x a)
where N(N c z(N b2 y b1)) x a = t -- gespiegelt
! Prägnant, aber ineffizient -
nur als Spezifikation, nicht als
Implementierung geeignet.
(Geschickter: add-Version, die new samt der Balance ermittelt.)
alp3-7.2
29
Löschen von Knoten:
erfordert i.a. wiederholtes Ausgleichen
mehrerer Unterbäume
Effiziente imperative Implementierung mit erweiterter Repräsentation:
 die Balance in jedem Knoten permanent speichern,
 Rückverkettung zum Eltern-Knoten.
( Abstraktion wenig geändert,
Invariante deutlich erweitert)
alp3-7.2
30
Ungünstigste Komplexität aller Operationen ist O(h).
Wie groß kann h - in Abhängigkeit von n - schlimmstenfalls werden?
(Zur Erinnerung: h(n) = log2(n+1) - 1 bei vollständigem Baum.)
Äquivalent:
wie klein kann n - in Abhängigkeit von h - schlimmstenfalls werden?
Die minimale Knotenanzahl eines AVL-Baums bezeichnen wir mit N(h):
alp3-7.2
31
N(0) = 1
N(1) = 2
N(2) = 4
N(3) = 7
N(4) = 12
alp3-7.2
 allgemein N(i) = 1 + N(i-1) + N(i-2)
32
Zur Erinnerung - die Fibonacci-Zahlen:
F(k) = F(k-1) + F(k-2) , F(0) = 0 , F(1) = 1
Beh.:
N(i) = F(i+3) - 1
Bew.:
(vollständige Induktion)
I.
i=0:
N(0) = 1 = 2 - 1 = F(3) - 1
i=1:
N(1) = 2 = 3 - 1 = F(4) - 1
II. Die Behauptung gelte bis zu einem bestimmten i≥1. Dann ist
N(i+1) = 1 + N(i) + N(i-1)
alp3-7.2
(nach Definition von N)
= 1 + F(i+3) - 1 + F(i+2) - 1
(nach Induktionsvor.)
= F(i+4) - 1
(nach Definition von F) QED.
33
Für jeden AVL-Baum mit n Knoten und Höhe h gilt also
n ≥ N(h) = F(h+3) - 1 ,
also
F(h+3) ≤ n+1
also
h ≤ ??
Explizite Darstellung der Fibonacci-Zahlen:
F(i) = ( Φi - Φi ) / √5
mit
Φ, Φ = (1 ± √5)/2 ≈ 1,62 (-0.62)
Somit gilt
( Φh+3 - Φh+3 ) / √5 ≤ n+1
also
Φh+3 ≤ √5 ( n + 1 + Φh+3/√5 ) ≤ √5 (n + 3/2)
|.....| ≤ 1/2 ( denn Φ ≈ -0.62 )
alp3-7.2
34
Logarithmieren ergibt
h + 3 ≤ logΦ √5 + logΦ (n + 3/2)
h ≤ logΦ n + C
h ≤ (logΦ 2) (log2 n) + C
≈ 1,44
 der Baum ist höchstens 44% höher (vgl. S. 17!)
als ein vollständiger Baum mit gleicher Knotenzahl
 garantierte Komplexität O(log n) für Suchen/Einfügen
(und auch für Löschen)
alp3-7.2
35
7.2.2.2 Rot-Schwarz-Bäume
(Bayer 1972)
= binäre Suchbäume mit folgender Invariante:
 Jeder Knoten ist entweder rot oder schwarz.
 null gilt als schwarzes Blatt.
 Die beiden Kinder eines roten Knotens sind schwarz.
 Alle Wege von einem gegebenen Knoten x zu einem Blatt
(null) enthalten die gleiche Anzahl schwarzer Knoten
- genannt Schwarz-Höhe des Knotens s(x) (ohne x selbst).
alp3-7.2
36
w
h=5
alp3-7.2
s(w) = 3
37
Beh.:
Die Höhe h eines Rot-Schwarz-Baums mit n eigentlichen
Knoten (ohne null) ist höchstens 2 log2 (n+1) (also
höchstens 100% höher als ein vollständiger Baum mit
gleicher Knotenzahl; vgl. S. 17, S. 35).
Bew.:
Wir betrachten einen Baum mit Wurzel w und SchwarzHöhe s(w). Da alle Wege von der Wurzel zu einem
null-Blatt mindestens s(w) eigentliche Knoten enthalten,
ist der Baum eine Erweiterung eines vollständigen Baums
der Höhe s(w)-1 und hat somit (6.1.3) mindestens 2s(w) - 1
eigentliche Knoten.
Weil jeder rote Knoten schwarze Kinder hat, gilt s(w) ≥ h/2.
Somit ist
n ≥ 2s(w) - 1 ≥ 2h/2 - 1 ,
also
n+1 ≥ 2h/2 ,
also
h ≤ 2 log2 (n+1)
QED.
alp3-7.2
38
Repräsentation:
- Geflecht
- Färbung
- schwarze Wurzel
- Verweis auf Vorgänger
alp3-7.2
Suchen:

Einfügen:
mit Umfärben und Rotieren
Löschen:
mit Umfärben und Rotieren
39
Einfügen eines Knotens x:
Schritt 1: Einfügen wie bei einfachem Suchbaum, rot färben;
fertig, wenn Vater schwarz.
Schritt 2: Solange x≠Wurzel und Vater rot:
falls
auch Onkel rot:
Vater und Onkel schwärzen,
Großvater röten,
x = Großvater;
sonst (Onkel ist schwarz):
falls x rechter Sohn: x = Vater, Linksrotation x;
(x ist linker Sohn)
Vater schwärzen,
Großvater röten,
Rechtsrotation Großvater. (Schleife bricht ab!)
Schritt 3: Wurzel schwärzen.
alp3-7.2
40
Java Collections Framework in java.util:
interface SortedSet<E> extends Set<E> ...
class TreeSet<E> implements SortedSet<E> ...
verwendet Rot-Schwarz-Baum;
die Iteratoren beachten die Ordnung auf E.
E extends Comparable<E> wird nicht gefordert: der Benutzer
kann wählen zwischen den Konstruktoren

public TreeSet()


public TreeSet(Comparator<? super E> c) (5.4.3, S. 10)
...
Bei 
müssen die eingefügten Elemente Comparable sein.
Bei 
wird mit Hilfe eines Comparator-Objekts verglichen, das eine
Methode int compare(T x1, T x2) bereitstellen muss.
alp3-7.2
41
Einige bemerkenswerte Methoden von SortedSet :
<T> T[] toArray(T[] a)
(von Set geerbt)
traversiert die Menge gemäß der vorgegebenen Ordnung,
speichert die Elemente im Feld a oder - falls a nicht
groß genug ist - in einem neu erzeugten Feld und liefert
dieses Feld
SortedSet<E> subSet(E fromElement, E toElement)
liefert eine Sicht (view) auf die Menge, die die Elemente
fromElement (inklusive) bis toElement (exklusive) enthält;
jede Änderung in der Menge wirkt sich auch in der
Sicht aus - und umgekehrt!
Iterator<E> descendingIterator()
(wie der Name sagt)
alp3-7.2
42
7.2.3 B-Bäume
(Bayer/McCreight 1972)
Def.:
Vielweg-Suchbaum (multi-way search tree):
Typ B<T> mit linear geordnetem T:
 ( ) ∈ B<T>
 Seien
xi ∈ T mit xi < xi+1
(i=1,2,...,n; n>0)
und
bk ∈ B<T>
(k=0,1,..,n) .
Dann ist b = (b0, x1, b1, x2, ... ,xn, bn) ∈ B<T> genau dann,
wenn mit x0 = A (z.B. -∞), xn+1 = Ω (z.B. +∞) gilt:
∀0≤i≤n ∀y∈α(bi) xi < y < xi+1
mit α( ) = { }
α( b0, x1, b1, x2, ... ,xn, bn ) = {x1,...,xn} ∪ ∪0≤i≤n α(bi)
alp3-7.2
43
Bemerkung: Damit wird einerseits ein Baum-Modell,
andererseits eine (abstrakte) Repräsentation für
Mengen spezifiziert - mit Abstraktionsfunktion α.
Die Definition enthält die Invariante.
Haskell:
alp3-7.2
data Ord t => Btree = Node[t][Btree t]
inv ...
abs ...
44
Bemerkung: Damit wird einerseits ein Baum-Modell,
andererseits eine (abstrakte) Repräsentation für
Mengen spezifiziert - mit Abstraktionsfunktion α.
Die Definition enthält die Invariante.
Haskell:
data Ord t => Btree = Node[t][Btree t]
inv ...
abs ...
(vergleiche mit 7.2.1.1, S. 4!)
o 10 o 20 o
o4o7o
o5o
alp3-7.2
o 15 o 17 o
o8o9o
o 27 o
o 22 o 23 o 24 o
o 21 o
45
Spezialfälle:

n=1 überall im Baum: Binärer Suchbaum

B-Baum der Ordnung m (m≥1):
a) Für jedes Nichtblatt sind alle Teilbäume nichtleer.
b) Alle Wege von der Wurzel zu einem Blatt sind gleich lang.
c) Jeder Knoten - außer der Wurzel - enthält zwischen
m und 2m Werte.
Motivation: externe Speicherung in Blöcken + Ausgeglichenheit

2-3-Baum: B-Baum der Ordnung 1
Motivation: Ausgeglichenheit
alp3-7.2
2-4-Baum: ist kein B-Baum,
aber verwandt mit Rot-Schwarz-Baum (s.u.)
46
Beispiel: B-Baum der Ordnung 2:
o 11 o
o 16 o 21 o 25 o
o5o8o
o1 o 2 o 3 o 4 o o6o7o
alp3-7.2
o 9 o 10 o
o 12 o 13 o 14 o
o 17 o 18 o 19 o 20 o o 22 o 23 o 24 o
47
Wie hoch wird ein B-Baum im ungünstigsten Fall?
 Minimale Belegung: m Werte in jeder Nichtwurzel, 1 Wert in Wurzel:
nmin = 1 + l + r
Im linken Teilbaum (und rechts entsprechend):
l = m + m(m+1) + m(m+1)2 + ... + m(m+1)h-1
= m ( 1 + (m+1) + ... + (m+1)h-1 )
= m ( (m+1)h - 1 ) / ( (m+1) - 1 )
= (m+1)h - 1
Also gilt
n ≥ nmin = 1 + l + r = 2(m+1)h - 1
also
(m+1)h ≤ (n+1)/2
und somit
h ≤ logm+1 ((n+1)/2) = (logm+1 2) (log2 (n+1) - 1) = O(log n)
1 0,63 0,50 0,43 0,39 . . . . .
alp3-7.2
48
Mengenoperationen auf B-Bäumen:
Suchen:
Von der Wurzel nach unten, mit Einschachteln (!)
in jedem Knoten (dort Aufwand ≤ log2 2m)
Einfügen:
Wenn Element nicht bereits vorhanden, endet Suche
in Blatt; dort einfügen, bei Überlauf Blatt splitten!
Löschen:
Wenn Element in Nichtblatt gefunden wird,
zunächst Nachfolger (in Blatt!) mit gefundenem
Wert vertauschen.
Wert in Blatt löschen, bei Unterlauf mit NachbarBlättern ausgleichen oder Blätter verschmelzen.
Wegen h ≤ O(log n) stets garantierte Komplexität O(log n).
alp3-7.2
49
Überlauf-Behandlung beim Einfügen:
Fall 1: Nichtwurzel läuft über (zunächst Blatt):
y z
x1 . . . . a . . . . x2m x2m+1
alp3-7.2
50
Überlauf-Behandlung beim Einfügen:
Fall 1: Nichtwurzel läuft über (zunächst Blatt):
y z
x1 . . . . a . . . . x2m x2m+1
y xm+1 z
x1 . . . xm
*
xm+2 . x2m+1
Höhe bleibt unverändert!
Wenn * überläuft, dort splitten usf. . . .
alp3-7.2
51
Fall 2: Wurzel läuft über:
xm+1
x1 . . . . a . . . . x2m x2m+1
x1 . . . xm
xm+2 . x2m+1
Baum zerfällt durch Splitten in zwei Teilbäume, die durch
neue Wurzel zusammengefasst werden. Höhe wächst um 1.
(Löschen: siehe z.B. Güting 8.1)
alp3-7.2
52
Herunterladen