Datenstrukturen

Werbung
Datenstrukturen
Mariano Zelke
Sommersemester 2012
Organisatorisches
I
Heute wird das 6. Übungsblatt ausgeteilt, das bis Donnerstag, den
5. Juli, 12:00 Uhr abzugeben ist.
I
Die Besprechung von Blatt 5 findet nur in der Woche 2.-6. Juli
(vorletzte Vorlesungswoche) statt.
I
In der Woche vom 9.-13. Juli (letzte Vorlesungswoche) findet die
Besprechung von Blatt 6 statt.
Mariano Zelke
Datenstrukturen
2/22
Organisatorisches
I
Am 27. Juli, 9:00 Uhr findet die Klausur in Hörsaal HV & HVI statt.
I
Denken Sie an eine rechtzeitige Anmeldung!
I
Sie dürfen ein handschriftlich beschriebenes DIN A4-Blatt als
Hilfsmittel mitbringen.
I
Im Zeitraum vor der Klausur (17.-24. Juli) finden Helpdesk-Termine
der Tutoren statt. Die genauen Termine erscheinen in Kürze auf der
VL-Website.
I
Dort finden Sie im gleichen Zeitraum auch die Liste der
Bonuspunkte für die Klausur. Prüfen Sie dann Ihren Eintrag!
Mariano Zelke
Datenstrukturen
3/22
Der abstrakte Datentyp Wörterbuch“
”
Ein Wörterbuch für eine gegebene Menge S besteht aus den folgenden
Operationen:
I
insert(x): Füge x zu S hinzu, d.h. setze S = S ∪ {x}.
I
remove(x): Entferne x aus S, d.h. setze S = S − {x}.
I
lookup(x): Finde heraus, ob x in liegt, und wenn ja, greife
gegebenenfalls auf den Datensatz von x zu.
I
In einer Firmendatenbank werden Kundendaten in der Form
(Kundenummer, Info) abgespeichert.
Die Kundennummer stellt den Schlüssel x dar.
I
I
I
I
Mariano Zelke
insert(x): Füge den Datensatz eines neuen Kunden mit
Kundenummer x ein.
remove(x): Entferne den Datensatz des entsprechenden Kunden.
lookup(x): Greife auf den Datensatz des Kunden mit Kundennummer
x zu.
Datenstrukturen
4/22
Datenstrukturen für Wörterbücher
I
Wie sollten statische Wörterbücher, also Wörterbücher die nur
lookup benutzen, implementiert werden?
I
I
Wir könnten die Menge der n gespeicherten Schlüssel zuerst
sortieren. Eine lookup-Operation gelingt dann in logarithmischer Zeit
durch binäre Suche.
Oder aber wir haben noch mehr Glück und haben eine schnell
berechenbare Namensfunktion, die für jeden Schlüssel die Position
des Schlüssels bestimmt.
Leider sind die interessanten Wörterbücher dynamisch.
I
Könnten wir Heaps benutzen? Das Einfügen gelingt mühelos, aber
schon das Suchen ist extrem mühselig.
Wir benötigen eine Datenstruktur, die schnell durchsuchbar und an
beliebigen Stellen modifizierbar ist.
Mariano Zelke
Datenstrukturen
5/22
Binäre Suchbäume
T sei ein geordneter binärer Baum. Jeder Knoten v von T speichert ein
Paar daten(v ) = (Schlüssel(v ), Info(v )).
T heißt binärer Suchbaum, wenn T die folgenden Eigenschaften hat:
(a) Für jeden Schlüsselwert x gibt es höchstens einen Knoten v mit
Schlüssel (v ) = x.
(b) Für jeden Knoten v , jeden Knoten vlinks im linken Teilbaum von v
und jeden Knoten vrechts im rechten Teilbaum von v gilt
Schlüssel(vlinks ) < Schlüssel(v ) < Schlüssel(vrechts ).
Binäre Suchbäume unterstützen die binäre Suche!
Mariano Zelke
Datenstrukturen
6/22
Operation lookup(x)
lookup(x) sucht im Binären Suchbaum folgendermaßen nach dem
Knoten mit Schlüssel x:
(1) Sei r die Wurzel des binären Suchbaums. Setze v = r .
/* Wir beginnen die Suche an der Wurzel.
*/
(2) Wenn wir am Knoten v angelangt sind, vergleichen wir
x mit Schlüssel(v ):
I
I
I
x = Schlüssel(v ): Wir haben den Schlüssel gefunden.
x < Schlüssel(v ): Wir suchen im linken Teilbaum weiter.
x > Schlüssel(v ): Diesmal muss im rechten Teilbaum
weitergesucht werden.
Lookup benötigt Zeit Θ(t), wobei t die Tiefe des Knoten ist, der den
Schlüssel x speichert.
Mariano Zelke
Datenstrukturen
7/22
Binäre Suchbäume: Insert
Suche zuerst nach x. Sollten wir x finden, überschreibe den alten
Info-Teil; sonst füge den Schlüssel dort ein, wo die Suche scheitert.
void bsbaum::insert (schluesseltyp x, infotyp info){
Knoten *Vater, *Zeiger;
Vater = Kopf; Zeiger = Kopf->rechts;
while ((Zeiger != 0) && (x != Zeiger->schluessel)){
Vater = Zeiger;
if (x < Zeiger->schluessel) Zeiger = Zeiger->links;
else Zeiger = Zeiger->rechts;
}
if (Zeiger == 0){
Zeiger = new Knoten (x, info, 0, 0);
if (x < Vater->schluessel) Vater->links = Zeiger;
else Vater->rechts = Zeiger;
}
else Zeiger->info = info;
}
Mariano Zelke
Datenstrukturen
8/22
Binäre Suchbäume: Remove
Zuerst suche den Schlüssel x. Wenn die Suche im Knoten v endet:
I
Wenn v ein Blatt ist: Entferne v .
I
Wenn v genau ein Kind w hat: Entferne v und mache den Vater
von v zum Vater von w .
Wenn v zwei Kinder hat: Ersetze v durch den kleinsten Schlüssel s
im rechten Teilbaum von v .
I
I
I
I
Mariano Zelke
Der Knoten u speichere den Schlüssel s.
u ist als linkester Knoten im rechten Teilbaum leicht zu finden.
u hat kein linkes Kind und kann damit sofort entfernt werden.
Datenstrukturen
9/22
Die Operationen eines binären Suchbaumes
I
I
Die Operationen insert und remove beginnen mit einer Suche nach
dem Schlüssel.
remove setzt den Suchprozess mit einer Suche nach dem kleinsten
Schlüssel im rechten Teilbaum fort.
Wir können mit binären Suchbäumen auch sortieren:
I
I
I
(Link)
Zuerst füge alle Schlüssel in einen leeren Suchbaum ein.
Danach bestimme die sortierte Reihenfolge durch einen
Inorder-Durchlauf.
lookup, insert und remove benötigen Zeit höchstens Tiefe des
Baums.
Aber: Die Tiefe kann sehr groß werden:
Die Folge insert(1,info), insert(2,info), insert(3,info), ...,
insert(n, info) erzeugt einen Baum der (maximalen) Tiefe n − 1.
Die minimale Tiefe ist blog2 nc, die maximale Tiefe n − 1.
Wie groß ist die erwartete Tiefe?
Mariano Zelke
Datenstrukturen
10/22
Erwartete vs. maximale Tiefe
I
Die erwartete Zeit für eine erfolgreiche Suche ist beweisbar
logarithmisch.
Es kann sogar gezeigt werden, dass die erwartete Tiefe
logarithmisch ist.
Also ist die erwartete Zeit für lookup, insert und remove
logarithmisch.
I
Trotzdem ist die worst-case Laufzeit intolerabel.
I
I
Mariano Zelke
Wir arbeiten weiter mit binären Suchbäumen,
garantieren aber durch zusätzliche Operationen, dass der Baum
tiefen-balanciert bleibt.
Datenstrukturen
11/22
AVL-Bäume
(Link)
Entwickelt 1962 von G. M. Adelson-Velski and E. M. Landis.
Ein binärer Suchbaum heißt AVL-Baum, wenn für jeden Knoten v mit
linkem Teilbaum TL (v ) und rechtem Teilbaum TR (v ) gilt:
| Tiefe(TL (v )) − Tiefe(TR (v )) | ≤ 1
b(v ) := Tiefe(TL (v )) − Tiefe(TR (v )) ist der Balance-Grad von v .
Für AVL-Bäume ist stets b(v ) ∈ {−1, 0, 1}.
Die zentralen Fragen:
I
Können wir stets Schlüssel so einfügen, dass der Absolutbetrag des
Balance-Grads höchstens Eins ist?
I
Wie tief kann ein AVL-Baum mit n Knoten werden?
Mariano Zelke
Datenstrukturen
12/22
Beispiele und Gegenbeispiele AVL-Bäume
und
sind AVL-Bäume.
und
Auch das ist ein
AVL-Baum:
Balancegrad = −1 X
Dies allerdings ist
kein AVL-Baum:
Mariano Zelke
Balancegrad = −2 ×
Datenstrukturen
13/22
Die Tiefe von AVL-Bäumen
min(t) sei die minimale Knotenzahl, die ein AVL-Baum der Tiefe t
mindestens besitzen muss.
I
min(0) = 1 und min(1) = 2.
I
Und es gilt die Rekursion min(t) = min(t − 1) + min(t − 2) + 1.
Wenn ein AVL-Baum die Tiefe t besitzt, dann muss ein Teilbaum die
Tiefe t − 1 besitzen und hat mindestens min(t − 1) Knoten.
Der andere Teilbaum hat mindestens Tiefe t − 2 und besitzt deshalb
mindestens min(t − 2) Knoten.
Es gilt min(t) ≥ 2t/2 . Die Tiefe eines AVL-Baums mit n Knoten ist
deshalb höchstens 2 · log2 n.
I
Die Behauptung ist richtig für t = 0 und t = 1.
I
min(t + 1) = min(t) + min(t − 1) + 1
≥ 2t/2 + 2(t−1)/2 + 1
≥ 2 · 2(t−1)/2 = 2(t+1)/2 .
Mariano Zelke
Datenstrukturen
14/22
Lookup, Remove und Insert
I
I
Da AVL-Bäume logarithmische Tiefe haben, ist die Laufzeit einer
lookup-Operation höchstens logarithmisch.
Wir drücken uns um die remove-Operation herum:
I
I
Wir führen nur eine lazy remove Operation durch und markieren
einen gelöschten Knoten als entfernt ohne ihn tatsächlich zu
entfernen.
Wenn allerdings mehr als 50 % aller Knoten markiert sind, dann
beginnt ein Großreinemachen:
Ein neuer AVL-Baum wird aus den nicht markierten Knoten des alten
Baumes durch Insert-Operationen aufgebaut.
Die Laufzeit für den Neuaufbaus ist groß, aber gegen die vielen
blitzschnellen remove-Operationen amortisiert.
Kritisch ist die Implementierung der insert-Operation.
Mariano Zelke
Datenstrukturen
15/22
Rotationen
(Link)
In einer Linksrotation ersetzt ein rechtes Kind den Vater. Der Vater wird
zum linken Kind.
v
w
w
v
T1
T3
T2
T3
T1
T2
Rechtsrotationen sind entsprechend definiert.
v
w
w
v
T3
T1
T1
T2
T2
T3
Die Ordnungseigenschaft binärer Suchbäume bleibt erhalten.
Mariano Zelke
Datenstrukturen
16/22
Die Insert-Operation
Um den Schlüssel x einzufügen, suche zuerst nach x und füge x am
Ende einer erfolglosen Suche ein.
I
An welchen Knoten ist jetzt möglicherweise die AVL-Eigenschaft
verletzt? Nur Knoten Suchpfads, also des Pfads von der Wurzel zum
frisch eingefügten Blatt, können betroffen sein!
I
Wir laufen deshalb den Suchpfad möglicherweise ganz zurück, um
die Balance-Eigenschaft zu reparieren.
Die Situation:
I
Wir sind bis zum Knoten u zurückgelaufen. Die AVL-Eigenschaft
gilt für u und alle Nachfahren von u.
I
Wenn die Reparatur fortzusetzen ist, müssen wir uns als Nächstes
um den Vater v von u kümmern.
I
w bezeichne den Großvater von u.
Mariano Zelke
Datenstrukturen
17/22
Der Zick-Zick Fall
w
v
u
A
Tiefe d
B
C
Fallannahme: Ein neues Blatt wurde im Teilbaum mit Wurzel u eingefügt
und Tiefe(C ) ≥ Tiefe(B).
I
Die Tiefe des Teilbaums von u muss um 1 angewachsen sein, denn
ansonsten können wir die Reparatur in v beenden.
I
Sei d die neue, um 1 größere Tiefe des Teilbaums von u.
Mariano Zelke
Datenstrukturen
18/22
Der Zick-Zick Fall, Fortsetzung
I
Tiefe(A) ≥ d + 1 ist unmöglich, da sonst b(v ) ≥ 2 vor Einfügen des
neuen Blattes gilt.
I
Wenn Tiefe(A) = d, brauchen wir nur den Balance-Grad b(v ) = 0
neu zu setzen. Die Reparatur kann abgebrochen werden, da der
Teilbaum mit Wurzel v seine Tiefe nicht verändert hat.
Wenn Tiefe(A) = d − 1, dann setze b(v ) = −1.
I
Diesmal müssen wir die Reparatur in w fortsetzen: Die Tiefe des
Teilbaums mit Wurzel v ist um 1 angestiegen.
I
Der Fall Tiefe(A) ≤ d − 3 kann nicht auftreten, da sonst b(v ) ≤ −2
vor Einfügen des neuen Blatts gilt.
Der Fall Tiefe(A) = d − 2 ist kritisch.
Mariano Zelke
Datenstrukturen
19/22
Tiefe(A) = d − 2
w
w
v
u
u
A
d −2
oder d −1
I
v
d −2
B
d −1
C
d −1
d −2
A
B
C
d −2
oder d −1
Es ist Tiefe(B) ≤ Tiefe(C ) nach Fallannahme und deshalb ist
Tiefe(C ) = d − 1. Da die AVL-Eigenschaft in u gilt, folgt
d − 2 = Tiefe(C ) − 1 ≤ Tiefe(B) ≤ d − 1.
Führe eine Linksrotation in v durch.
I
Die AVL-Eigenschaft gilt somit nach der Rotation für u und v .
Setze b(u) und b(v ) entsprechend und fahre fort, wenn der neue
Teilbaum von u tiefer ist als der alte Teilbaum von v .
Mariano Zelke
Datenstrukturen
20/22
Der Zack-Zack Fall
w
v
u
C
A
B
Fallannahme: Ein neues Blatt wurde im Teilbaum mit Wurzel u eingefügt
und Tiefe(A) ≥ Tiefe(B).
Der Zack-Zack Fall wird analog zum Zick-Zick Fall behandelt.
Mariano Zelke
Datenstrukturen
21/22
Beispiel
Nach dem Einfügen
der Schlüssel
3, 9, 2, 8
2
entsteht dieser
AVL-Baum:
Nach dem Einfügen
des weiteren
Schlüssels 6
2
ist die Balance
bei 9 verletzt.
3
9
8
3
b=2
9
8
6
Damit ist der
Zack-Zack-Fall
erreicht, der
2
eine Rechtsrotation bei 9 auslöst.
8
6
Der jetzt entstandene Zick-Zick-Fall
löst eine Linksrotation bei 3 aus.
Dabei wechselt die 6 vom
3
rechten Teilbaum des rotierten
Knotens in den linken.
2
Mariano Zelke
Wird nun noch
Schlüssel 11
eingefügt, so
ist die Balance
bei 3 verletzt.
3
9
3 b = −2
2
8
6
9
11
8
9
6
Datenstrukturen
11
22/22
Herunterladen