12. 5. 2011

Werbung
Was bisher geschah
rekursive Datenstrukturen:
I lineare Datenstrukturen:
I
I
I
I
verkettete Liste
Stack
Queue
hierarchische Datenstrukturen: Bäume
I
I
I
allgemeine Bäume
Binäre Bäume
Unäre Bäume = Listen
132
Wiederholung ADT Menge
Ziel: Verwaltung einer (Teilmenge einer total geordneten) Menge
Extra: sortierte Ausgabe
ADT (Spezifikation):
I
Sorten: Bool, Element, Menge (kurz für MengehElementi),
Folge (kurz für FolgehElementi)
I
Signatur:
emptyset :
isempty :
Menge →
add :
Menge × Element →
remove :
Menge × Element →
contains : Menge × Element →
sorted :
Menge →
I
Menge
Bool
Menge
Menge
Bool
Folge
Axiome, z.B. ∀s ∈ Menge, ∀E ∈ Element):
contains(add(s, e), e) = t, contains(remove(s, e), e) = f,
isempty(add(s, e)) = f, add(add(s, e), e0 ) = add(add(s, e0 ), e),
add(add(s, e), e) = add(s, e), . . .
head(sorted(s)) < head(tail(sorted(s))), . . .
133
Wiederholung: ADT Binärbaum
mit Knotenmarkierungen vom Typ Element
I
Sorten: Bool, Element, BinTree (kurz für BinTreehElementi),
I
Signatur:
⊥:
isEmpty :
BinTree →
Node : Element × BinTree × BinTree →
Left, Right :
BinTree →
Val :
BinTree →
t, f :
I
BinTree
Bool
BinTree
BinTree
Element
Bool
Axiome, z.B. ∀v ∈ Element ∀t1 ∈ BinTree ∀t2 ∈ BinTree:
isEmpty(⊥) :
= t
isEmpty(Node(v , t1 , t2 )) :
= f
Left(Node(v , t1 , t2 )) :
= t1
Right(Node(v , t1 , t2 )) :
= t2
Val(Node(v , t1 , t2 )) :
= v
134
Binäre Suchbäume
Binärer Baum t hat die Suchbaum-Eigenschaft gdw.
für jeden Teilbaum t 0 = Node(x, l, r ) von t gilt:
I
für jeden Schlüsselwert y eines inneren Knotens von l gilt
y <x
I
für jeden Schlüsselwert v 0 eines inneren Knotens von r gilt
x <y
Ergebnis der Inorder-Durchquerung jedes binären
Suchbaumes ist eine aufsteigend sortierte Folge
Sortierte Ausgabe aller im Suchbaum mit n Knoten enthaltenen
Daten in O(n)
135
Suche in binären Suchbäumen
Spezifikation:
gegeben: binärer Suchbaum t (Wurzel)
Schlüsselwert x
gesucht: Knoten t 0 in t mit Val(t 0 ) = x,
⊥, falls t keinen Knoten mit dem Schlüsselwert x enthält
contains(t,x):
if (x = Val(t) || isEmpty(t))
return x
else
if x < Val(t)
return contains(Left(t), x)
else
return contains(Right(t), x)
136
Laufzeit der Suche
Laufzeit von contains(t,x) abhängig von
Anzahl n der Knoten in t:
I
falls t einen Knoten t 0 mit Val(t 0 ) = x enthält:
Tiefe des Knotens t 0
I
falls t keine Knoten t 0 mit Val(t 0 ) = x enthält:
Tiefe des Baumes t 0
höchstens Tiefe des Baumes t (log n ≤ tiefe(t) ≤ n)
Suche im Suchbaum t in O(tiefe(t))
(i.A. schneller als lineare Suche in einer Folge)
137
Anwendung von Suchbäumen
Binäre Suchbäume sind geeignet zum Speichern und
schnellen Wiederfinden von Daten anhand ihnen zugeordneten
Schlüsselwerten
Binäre Suchbäume:
I
Implementierung des ADT Menge für linear geordnete
Mengen
I
Implementierung von Wörterbüchern:
Daten mit zugeordnetem Schlüsselwert (aus einer linear
geordneten Menge)
Operationen: contains, add, remove
z.B. Telefonbucheinträge über Namen (alphabetisch
geordnet), Studenten über Studentennummern
138
Extremwerte in binären Suchbäumen
Spezifikation:
gegeben: binärer Suchbaum t (Wurzel)
gesucht: Knoten in t mit dem minimalen Schlüsselwert,
d.h. Knoten s, wobei für jeden Knoten r gilt Val(s) ≤ Val(r)
⊥, falls t = ⊥
Minimaler Schlüsselwert binären Suchbaum steht im äußeren linken
Knoten
minimum(t):
if isEmpty(t)
return Nil
else
if Left(t) = Nil
return t
else
return minimum(Left(t))
Minimum lässt sich ohne Schlüsselvergleich bestimmen.
Laufzeit höchstens Tiefe des Baumes t (log n ≤ tiefe(t) ≤ n)
Minimum-Suche im Suchbaum mit n Knoten in O(tiefe(t))
Maximum analog
139
Einfügen in binäre Suchbäume
Spezifikation:
gegeben: binärer Suchbaum t mit den Schlüsseln {x1 , . . . , xn }
Schlüsselwert x
gesucht: binärer Suchbaum t 0 mit den Schlüsseln {x1 , . . . , xn , x}
add(t,x):
if isEmpty(t)
return Node(x,Nil,Nil)
else
if x < Val(t)
return add (Left(t),x)
else
return add (Right(t),x)
140
Iteriertes Einfügen
Beim Einfügen der Elemente der Menge {2, 3, 5, 7} in
verschiedenen Reihenfolgen in ⊥ entstehen i.A. veschiedene
Bäume
Beispiel (Tafel):
I
3,7,5,2
I
5,3,7,2
I
2,3,5,7
Daraus lässt sich ein Sortierverfahren ableiten.
Sortieren durch Einfügen in binären Suchbaum:
1. schrittweises Einfügen aller Elemente einer Menge in
einen zu Beginn leeren binären Suchbaum
2. Sortierte Ausgabe durch Inorder-Durchquerung des so
entstandenen binären Suchbaumes
141
Laufzeit Einfügen
Idee: neues Blatt mit Schlüsselwert x wird dort eingefügt, wo es
der Suchalgorithmus finden würde
selbe Laufzeit wie Suche:
höchstens Tiefe des Baumes t (log n ≤ tiefe(t) ≤ n)
Einfügen in einen binären Suchbaum mit n Knoten in O(tiefe(t))
142
Löschen aus binären Suchbäumen
Spezifikation:
gegeben: binärer Suchbaum t mit den Schlüsseln {x1 , . . . , xn }
Schlüsselwert x
gesucht: binärer Suchbaum t 0 mit den Schlüsseln {x1 , . . . , xn } \ {x}
mögliche Fälle:
1. x kommt nicht in t vor: Ergebnis t 0 = t
2. x ist Schlüsselwert eines Knotens s in t mit zwei leeren Kindern:
Löschen des Knotens s (Ersetzen durch ⊥)
3. x ist Schlüsselwert eines Knotens s in t mit einem leeren Kind:
Ersetzen des Knotens s durch sein einziges nichtleeres Kind
4. x ist Schlüsselwert eines Knotens s in t mit zwei nichtleeren
Kindern:
Tausch der Schlüsselwerte in s und dem linken äußeren Knoten
r des rechten Kindes von s (Minimum des rechten Kindes)
Löschen des Knotens r (rekursiv)
Warum hat der so entstandene Baum die Suchbaumeigenschaft?
Laufzeit für Löschen aus binärem Suchbaum t: O(tiefe(t))
143
Laufzeiten
I
contains
I
add
I
remove
in O(tiefe(t))
Laufzeiten in Abhängigkeit von der Knotenzahl n = size(t):
Extremfälle:
I
für Pfade (entartete Bäume) tiefe(t) = size(t)
Laufzeit der Operationen O(n)
I
für balancierte Bäume tiefe(t) = log size(t)
Laufzeit der Operationen O(log n)
144
Balancierte binäre Suchbäume
Laufzeit für Suche, Einfügen, Löschen in Baum t mit n Knoten:
O(tiefe(t))
Ziel: Laufzeit für Suche, Einfügen, Löschen O(log(n))
Idee: balancierte Suchbäume, in denen die Höhe jedes Blattes
(etwa) gleich ist
145
Vollständig balancierte Bäume
Binärer Suchbaum t heißt vollständig balanciert, wenn in jedem
Knoten Node(x, l, r ) in t gilt:
|size(l) − size(r )| ≤ 1
Tiefe jedes Knotens ≤ blog(n)c + 1
Beispiel (Tafel)
146
Operationen in vollständig balancierten Bäumen
I
Suche O(log(n))
I
Einfügen und Löschen:
zunächst Einfüge- und Löschoperation für binäre
Suchbäume
danach muss vollständige Balance wieder hergestellt
werden (Rebalancieren)
i.A. aufwendig
147
AVL-Bäume
(Adelson-Velskii, Landis, 1962)
Binärer Suchbaum t heißt AVL-Baum, wenn in jedem Knoten
Node(x, l, r ) in t gilt:
|tiefe(r ) − tiefe(l)| ≤ 1
(AVL-Eigenschaft)
Optimierung:
Speichern der Balance tiefe(r ) − tiefe(l) in jedem Knoten
erlaubt schnellen Test
I
Suche O(log(n))
I
Einfügen und Löschen:
zunächst Einfügeoperationen für binäre Suchbäume
danach Wiederherstellung der AVL-Eigenschaft
148
Rebalancieren in AVL-Bäumen
nach Einfügen (als neues Blatt) oder
Löschen (rekursives Ersetzen durch einziges Kind oder
Minimum im rechten Teilbaum)
Verletzung der AVL-Eigenschaft im Knoten (x, l, r ) mit
|tiefe(l) − tiefe(r )| = 2
evtl. Verletzung in mehreren Vorgängern des eingefügten
Knotens
Fälle vor dem Einfügen:
I
I
Balance 0: tiefe(l) = tiefe(r )
nach Einfügen keine Verletzung der AVL-Eigenschaft
Balance −1: tiefe(l) = tiefe(r ) + 1
I
I
I
nach Einfügen in r keine Verletzung der AVL-Eigenschaft
nach Einfügen in l evtl. Verletzung der AVL-Eigenschaft
Balance +1: tiefe(l) + 1 = tiefe(r )
I
I
nach Einfügen in l keine Verletzung der AVL-Eigenschaft
nach Einfügen in r evtl. Verletzung der AVL-Eigenschaft
(symmetrisch)
149
(Einfache) Rotation in AVL-Bäumen
bei Verletzungen der AVL-Eigenschaft im tiefsten Knoten k der
Form
k = Node(x, Node(y , l, r ), s)
mit tiefe(l) − 1 = tiefe(r ) = tiefe(s)
(Balance von x: −2, Balance von y: −1)
Ersetzung von k durch lrotate(k ) = Node(y , l, Node(x, r , s))
(Linksrotation)
analog (symmetrischer Fall):
Verletzungen der AVL-Eigenschaft im tiefsten Knoten k der
Form
k = Node(x, l, Node(y , r , s))
mit tiefe(l) = tiefe(r ) = tiefe(s) − 1
(Balance von x: +2, Balance von y: +1)
Ersetzung von k durch rrotate(k ) = Node(y , Node(x, l, r ), s)
(Rechtsrotation)
150
Doppel-Rotation in AVL-Bäumen
bei Verletzungen der AVL-Eigenschaft im tiefsten Knoten der Form
k = Node(x, Node(y , l, Node(z, r , s)), t) mit Balance x: −2, y: +1
Ersetzung von k durch
lrrotate(k ) = Node(z, Node(y , l, r ), Node(x, s, t))
Links-Rechts-Rotation: zwei aufeinanderfolgende Rotationen:
rrotate(Left(k )) danach lrotate(k )
symmetrischer Fall:
k = Node(x, t, Node(y , Node(z, l, r ), s)) mit (Balance x: +2, y: −1)
Ersetzung von k durch
rlrotate(k ) = Node(z, Node(x, t, l), Node(y , r , s))
Rechts-Links-Rotation: zwei aufeinanderfolgende Rotationen:
lrotate(Right(k )) danach rrotate(k )
151
Laufzeit der Operationen in AVL-Bäumen
Rebalancieren (Rotationen): O(1)
Tiefe von AVL-Bäumen mit n Knoten: O(log(n))
I
Suche: O(log(n))
I
Einfügen: O(log(n))
I
Löschen: O(log(n))
152
Herunterladen