Datenstrukturen - Zentrum für Angewandte Informatik der Universität

Werbung
Informatik I
2. Kapitel
Elementare Datenstrukturen
Rainer Schrader
Zentrum für Angewandte Informatik Köln
28. Mai 2008
1/1
2/1
Datenstrukturen
Datenstrukturen
• bisher haben wir nur Arrays verwendet,
• effiziente Algorithmen brauchen geeignete Datenstruktur
Gliederung
• Einführung
• abstrakte Datentypen
• Beispiel: Suche im Telefonbuch
• gegeben: Name
• gesucht: Telefonnummer
• Listen
• Stacks und queues
• Anwendungsbeispiele
• Bäume
•
; einfach
• gegeben: Telefonnummer
• gesucht: Name
•
3/1
; praktisch unmöglich
4/1
Datenstrukturen
Datenstrukturen
• offensichtlich ist das Problem in der Zeit Θ(n) lösbar,
• aber es geht besser, wenn man viele Anfragen zu einem festen M hat
Nichttriviales Beispiel: Posamp-Problem
• gegeben:
Informelle Beschreibung einer geeigneten Datenstruktur
• n Orte (Postämter) M = {p1 , p2 , . . . , pn }
• ihre kartesischen Koordinaten pi = (xi , yi )
• sei VR(pj ) die Menge aller Punkte der Ebene, die näher an pj als an
• zusätzlicher Ort p0 = (x0 , y0 )
• also
jedem anderen Punkt in M liegen,
n
o
VR(pj ) = q ∈ R2 | d (q, pj ) ≤ d (q, pi ) ∀i ∈ {1, 2, . . . n} .
• gesucht:
• das zu p0 nächstgelegene Postamt pk , d.h.
• d (p0 , pk ) ≤ d (p0 , pi ) für alle i ∈ {1, . . . , n} mit
p
• d (pi , pj ) = (xi − xj )2 + (yi − yj )2 (Euklidische Distanz)
• VR(pj ) ist die Voronoi-Region von pj
• das Voronoi-Diagramm ist die Menge
VD(M ) = {VR(pj ) | j ∈ {1, 2, . . . , n}}
5/1
6/1
Datenstrukturen
Datenstrukturen
Beispiel:
Idee eines effizienten Verfahrens für eine beliebige Anfrage
• (AD) Aufbau der Datenstruktur:
bestimme das Voronoi-Diagramm VD(M ).
• (BA) Beantwortung einer Anfrage:
für eine Anfrage p0 : bestimme VR(pk ) mit p0 ∈ VR(pk ).
man kann zeigen: (evtl. Kapitel 10)
• (AD) benötigt O(n log n) Zeit.
• (BA) benötigt O(log n) Zeit.
7/1
8/1
Datenstrukturen
Datenstrukturen
• Datenstrukturen sollen Operationen auf Objekten unterstützen
• evtl. verschiedene Datenstrukturen für verschiedene Operationen
Gliederung
• Einführung
• abstrakte Datentypen
• evtl. Verknüpfung von Objekten und Operationen (Klassen)
• Listen
• Stacks und queues
• Anwendungsbeispiele
• Bäume
Abstrakter Datentyp (ADT)
besteht aus
• einer oder mehreren Mengen von Objekten
• und darauf definierten Operationen
10 / 1
9/1
Datenstrukturen
Datenstrukturen
Beispiel Postamtproblem :
Beispiel: Formulierung eines Algorithmus mit Hilfe von ADT
• Objekte:
Aufgabe:
• endliche Mengen M von Punkten in der Ebene
• gegeben eine endliche Menge M von Punkten in der Ebene
• finde ein Paar p0 , q0 von Punkten mit minimalem Abstand d (p0 , q0 )
• ein Punkt p0 in der Ebene
• Operation
ADT:
• „nächster Nachbar“: ordne dem Punkt p0 einen nächstgelegen
Punkt aus M zu
• Objekt: endliche Punktmenge M
• Operationen:
• Mögliche zusätzliche Operationen:
• füge einen Punkt zu M hinzu
• nächster Nachbar
• Distanz zweier Punkte
• entferne einen Punkt aus M
• Kardinalität von M
• M := M r {p}
Wir haben jedesmal einen anderen ADT.
11 / 1
12 / 1
Datenstrukturen
Datenstrukturen
Algorithmus nearest_neighbours(M)
liefert p0 , q0 ∈ M mit minimaler Distanz d (p0 , q0 )
falls M = ∅ oder |M | = 1, so sind p0 und q0 nicht definiert
Implementierung (Realisierung als Computerprogramm)
falls |M | ≥ 2:
• wähle zwei Punkte p0 , q0 ∈ M
• berechne die Distanz dist := d (p0 , q0 )
• Auswahl von Datenstrukturen für die Objektmengen
• Auswahl von Algorithmen für die Operationen
• for all p ∈ M do
• bestimme einen nächsten Nachbarn q ∈ M r {p}
• berechne die Distanz d (p, q)
• falls d (p, q) < dist, so setze:
• p0 := p; q0 := q; dist := d (p, q).
• end do
14 / 1
13 / 1
Datenstrukturen
Datenstrukturen
Lineare Liste
• Objekte:
Gliederung
• Menge aller endlichen Folgen eines gegebenen Grundtyps
• die Schreibweise für eine lineare Liste ist
• Einführung
• abstrakte Datentypen
L = ha1 , a2 , . . . , an i,
• Listen
• Stacks und queues
• Anwendungsbeispiele
• Bäume
• hierbei ist L = hi die „leere Liste“
• verschiedene Operationen
• im Folgenden ist L eine lineare Liste, x vom Grundtyp und
p eine Position
15 / 1
16 / 1
Datenstrukturen
Datenstrukturen
Füge_ein(x,p,L)
Entferne(p,L)
• falls L = ha1 , a2 , . . . , ap−1 , ap , ap+1 , . . . , an i 6= hi :
L → ha1 , a2 , . . . , ap−1 , x , ap , ap+1 , . . . , an i
L → hx , a1 , a2 , . . . , an i
L → ha1 , a2 , . . . , an , x i
für L 6= hi und 1 ≤ p ≤ n gilt:
für 1 < p ≤ n
• aus
ha1 , a2 , . . . , ap−1 , ap , ap+1 , . . . an i
für p = 1
wird
für p = n + 1
ha1 , a2 , . . . , ap−1 , ap+1 , . . . an i.
• falls L = hi und p = 1:
hi → hx i
• undefiniert sonst
• undefiniert sonst
18 / 1
17 / 1
Datenstrukturen
Datenstrukturen
Suche(x,L)
Verkette(L,L1,L2)
• liefert eine Position p in L = ha1 , a2 , . . . , an i mit ap = x , falls diese
existiert,
• liefert für
• sonst 0.
L1 = ha1 , a2 , . . . , an1 i und L2 = hb1 , b2 , . . . , bn2 i
die Liste
Zugriff(p,L)
L = ha1 , a2 , . . . , an1 , b1 , b2 , . . . , bn2 i.
• liefert ap in L = ha1 , a2 , . . . , an i, falls 1 ≤ p ≤ n,
• sonst undefiniert.
19 / 1
20 / 1
Datenstrukturen
Datenstrukturen
Gliederung
• Einführung
• abstrakte Datentypen
Darstellungen linearer Listen
• sequentielle Darstellung
• Listen
• zusammenhängende Speicherung als Feld mit direktem Zugriff
• sequentielle Listen
• verkettete Listen
• verkettete Listen
• Stacks und queues
• Anwendungsbeispiele
• verteilte Speicherung, durch Zeiger verknüpft
• Bäume
21 / 1
22 / 1
Datenstrukturen
Datenstrukturen
Füge_ein(x,p,L)
Sequentielle Listen
\\ fügt x als neues Element an Stelle p ein, falls p
zulässig, gibt andernfalls Fehlermeldung aus
Eine sequentiell gespeicherte Liste L besteht aus:
• einer (hinreichend großen) Konstanten maxelzahl
• einem Feld element[0, . . . , maxelzahl]
if (L.elzahl = L.maxelzahl or p < 1 or p > L.elzahl + 1)
print ”Fehler”
else
for i = L.elzahl down to p do
L.element(i+1) = L.element(i)
end do
L.element(p) = x
L.elzahl = L.elzahl+1
end if
• der aktuellen Listengröße elzahl
Ist 0 < L.elzahl ≤ L.maxelzahl, so ist in
L.element(1), . . . , L.element(elzahl)
die aktuelle Liste abgelegt.
23 / 1
24 / 1
Datenstrukturen
Datenstrukturen
Entferne(p,L)
Suche(x,L)
\\ entfernt das p. Element, falls p zulässig, gibt
andernfalls Fehlermeldung aus
\\ liefert die höchste Position, an der x in L vorkommt,
0 sonst
if (L.elzahl = 0 or p < 1 or p > L.elzahl)
print ”Fehler”
else
L.elzahl = L.elzahl - 1
for i = p to L.elzahl do
L.element(i) = L.element(i+1)
end do
end if
L.element(0) = x
i = L.elzahl
while (L.element(i) 6= x) do
i = i-1
end while
return i
26 / 1
25 / 1
Datenstrukturen
Datenstrukturen
Zugriff(p,L)
Verkette(L,L1,L2)
\\ gibt das p. Element aus, falls p zulässig, gibt
andernfalls Fehlermeldung aus
\\ hänge L2 an L1 an, falls L1 ausreichend lang
if (L1.elzahl + L2.elzahl > L1.maxelzahl)
print ”Fehler”
else
if (L2.elzahl > 0) then do
for i = 1 to L2.elzahl do
L1.element(L1.elzahl + i) = L2.element(i)
end do
end if
L1.elzahl = L1.elzahl + L2.elzahl
end if
if (p < 1 or p > L.elzahl)
print ”Fehler”
else
return L.element(p)
end if
27 / 1
28 / 1
Datenstrukturen
Datenstrukturen
Gliederung
Laufzeiten
• Einführung
• abstrakte Datentypen
Sei n die Länge der Liste
Operation
suche
füge_ein
entferne
Zugriff
verkette
Zeit
• Listen
O(n)
O(n)
O(n)
O(1)
O(n2 )
• sequentielle Listen
• verkettete Listen
• Stacks und queues
• Anwendungsbeispiele
• Bäume
29 / 1
30 / 1
Datenstrukturen
Datenstrukturen
eine mögliche Implementation von verketteten Listen
eine Liste L = ha1 , a2 , . . . , an i lässt sich wie folgt darstellen:
• eine verkettete Liste wird „kreuz und quer“ im Speicher abgelegt
a1
• eine Liste ist gegeben als eine Folge von Knoten
• jeder Knoten besteht aus
a2
...
an
DUMMY-Elemente
head
tail
(a) einem Listenelement dat
(b) einem Zeiger next auf das nächste Listenelement
• zwei Dummy-Elemente als „Kopf“ und „Schwanz“ der Liste
eine leere Liste sieht dann wie folgt aus:
• einem Zeiger head auf das Kopfelement
• einem Zeiger tail auf das Schwanzelement
• der Zeiger des Schwanzelements zeigt auf das vorangehende Element
head
31 / 1
tail
32 / 1
Datenstrukturen
Datenstrukturen
Füge_ein(x,p,L)
Vereinbarung:
• Zeiger von x := Zeiger von Element p − 1
• Zeiger von Element p − 1 := Zeiger auf x
Die Position p ist gegeben durch einen Zeiger auf den Knoten, dessen Zeiger
auf das p.te Listenelement zeigt:
Skizze für Füge_ein:
ap
vorher:
ap−1
ap
p
p
nachher:
ap−1
ap
Die Listenoperationen lassen sich wie folgt durchführen:
x
Wir überprüfen hier nicht, ob p ein legaler Zeiger ist.
33 / 1
34 / 1
Datenstrukturen
Datenstrukturen
Entferne(p,L)
• Zeiger von p − 1 := Zeiger von Element p
• (entferne Knoten p)
Suche(x,L) liefert die Position des ersten Listenelements mit dem
Schlüssel x , falls es existiert, sonst NULL.
Suche(x,L)
Skizze für Entferne(p,L):
vorher:
ap−1
ap
• kopiere x in das Datumsfeld des Schwanzes
• beginnend mit dem Kopfelement suche nach x
ap+1
• wird x in einem Element p gefunden:
p
nachher:
ap−1
• ist p nicht das Schwanzelement: gib Zeiger auf p zurück
• andernfalls gib Null zurück
ap+1
Wir überprüfen hier nicht, ob p ein legaler Zeiger ist.
35 / 1
36 / 1
Datenstrukturen
Datenstrukturen
Doppelte Verkettung
Verkette(L,L1,L2)
• tail1 zeigt auf das Schwanzelement von L1
• der Zeiger dieses Schwanzelements zeigt auf das letzte Element von L1
• biege dessen Zeiger auf den Zeiger des Kopfelements von L2 um
• entferne das Schwanzelement von L1 und das Kopfelement von L2
• manchmal ist es hilfreich, eine Kette auch „rückwärts“ durchlaufen zu
können
• wir verwenden einen zusätzlichen Zeiger auf den jeweiligen Vorgänger
• hier können wir die Position p durch einen Zeiger auf den Knoten mit
dat-Komponente ap definieren
Skizze für Verkette:
head1
head
ap−1
...
ap
ap+1
p
tail1
Beispiel für Entferne:
tail
...
head2
ap−1
ap+1
tail2
37 / 1
38 / 1
Datenstrukturen
Datenstrukturen
Zusammenfassung:
Gliederung
Operation
suche
füge_ein
entferne
Zugriff
verkette
sequentiell
Θ(n)
Θ(n)
Θ(n)
Θ(1)
Θ(n2 )
verkettet
Θ(n)
Θ(1)
Θ(1)
Θ(1)
Θ(1)
• Einführung
• abstrakte Datentypen
• Listen
• Stacks und queues
• Anwendungsbeispiele
• Bäume
39 / 1
40 / 1
Datenstrukturen
Datenstrukturen
Stapel
Schlange
• ist eine spezielle Liste
• Einfügen und Entfernen nur am Anfang der Liste erlaubt
• ist eine spezielle Liste
• Einfügen nur am Ende, Entfernen nur am Anfang der Liste
• „LIFO“: last-in-first-out
• „FIFO“: first-in-first-out
• Operationen:
• Operationen:
• empty(L): teste, ob L leer ist
• pop(L,x): setze x := a1 ; entferne a1
• enqueue(L,x): füge x am Ende von L ein
• dequeue(L,x): setze x = a1 , entferne a1 aus L
• empty(L): teste, ob L leer ist
• top(L,x): setze x := a1 (ohne zu entfernen)
• top(L,x): setze x := a1 (ohne zu entfernen)
• push(L,x): füge x am Anfang von L ein
42 / 1
41 / 1
Datenstrukturen
Datenstrukturen
• alle Stapel-, Schlangen- und Doppelschlangen-Operationen können mit
konstanter Laufzeit implementieren werden
Doppelschlangen
• sowohl sequentiell als auch verkettet
• beide push- und pop-Operationen sowie
• empty(L), top(L,x), bottom(L,x)
• Anwendung als Warteschlangen und Vorrangwarteschlangen
• sequentiell implementierte Schlangen sollten „zyklisch“ verwaltet
werden
43 / 1
44 / 1
Datenstrukturen
Datenstrukturen
Skizzen für sequentielle Speicherung
Stapel
(Doppel−)Schlange
maxn
frei
1111
0000
0000
1111
Stapel
1111
0000
0000
1111
0000
1111
0000
1111
1111111111111111111
0000000000000000000
0000000000000000000
1111111111111111111
0000000000000000000
1111111111111111111
Anfang
0000000000000000000
1111111111111111111
0000000000000000000
1111111111111111111
0000000000000000000
1111111111111111111
0000000000000000000
1111111111111111111
0000000000000000000 maxn
1111111111111111111
frei
0000000000000000000
1111111111111111111
0000000000000000000
1111111111111111111
0000000000000000000 0
1111111111111111111
0000000000000000000
1111111111111111111
0000000000000000000
1111111111111111111
00000000000000000001
Ende1111111111111111111
0000000000000000000
1111111111111111111
0000000000000000000
1111111111111111111
0000000000000000000
1111111111111111111
0000000000000000000
1111111111111111111
0000000000000000000
1111111111111111111
Gliederung
• Einführung
• abstrakte Datentypen
• Listen
• Stacks und queues
• Anwendungsbeispiele
• Bäume
45 / 1
Datenstrukturen
46 / 1
Datenstrukturen
Definition eines wohlgeformten Klammerausdrucks
Anwendung eines Stapels: wohlgeformte Klammerausdrücke
(1) () ist ein wohlgeformter Klammerausdruck
• Test, ob Klammerfolgen wohlgeformt sind
• Beispiel:
(()())
• Beispiel:
((())(
(2) sind w1 und w2 wohlgeformte Klammerausdrücke, so ist auch
w1 w2 ein wohlgeformter Klammerausdruck
√
(3) mit w ist auch (w ) ein wohlgeformter Klammerausdruck
(4) nur die nach (1) bis (3) gebildeten Zeichenketten sind wohlgeformte
Klammerausdrücke
47 / 1
48 / 1
Datenstrukturen
Datenstrukturen
Anwendung eines Stapels: Test von Klammerausdrücken
Anwendung von Listen: Datenstruktur für Projektplanungen
• durchsuche Zeichenkette von links nach rechts
gegeben: n Teilprojekte eines Projekts (Bauprojekt, Kfz-Fertigung, …) mit
• Zeitangaben über die Dauer der Teilprojekte
• Reihenfolgebedingungen zwischen einzelnen Teilprojekten
• wird „(“ gefunden: lege sie auf Stapel
• wird „)“ gefunden:
bestimme:
• ist Stapel leer ; nicht wohlgeformt
• Gesamtdauer
• Pufferzeiten
• andernfalls entferne obere Klammer vom Stapel
• am Ende:
• kritische Pfade
• …
• ist Stapel nicht leer ; nicht wohlgeformt
• andernfalls ; wohlgeformt
49 / 1
50 / 1
Datenstrukturen
Datenstrukturen
12
Beispiel mit 4 Teilprojekten
• TP1 : Dauer: 12
• TP2 : Dauer: 6, muss warten auf Beendigung von TP1 , TP3
• TP3 : Dauer: 9, muss warten auf Beendigung von TP1
• TP4 : Dauer: 7, muss warten auf Beendigung von TP2 , TP3
6
1
2
3
4
9
7
Darstellung mit verketteten Listen
12
6
1
3
9
2
4
1
2
2
4
3
2
4
NIL
3
4
7
51 / 1
52 / 1
Datenstrukturen
Datenstrukturen
gerichteter Graph G = (V , E )
(ungerichteter) Graph G = (V , E )
besteht aus:
besteht aus:
• einer endlichen Menge V von Knoten
• einer endlichen Menge E von gerichteten Kanten
• jeder Kante e ∈ E entspricht ein Paar u, v von Knoten
• einer endlichen Menge V von Knoten
• einer endlichen Menge E von Kanten
• jeder Kante e ∈ E entspricht ein Paar u, v von Knoten
• u, v sind die Endknoten von e
• u Startknoten, v Endknoten von e
• wir identifizieren e und das Knotenpaar, d.h. e = (u, v )
• wir identifizieren e und das Knotenpaar, d.h. e = {u, v }
• Veranschaulichung wie vorher:
• Veranschaulichung:
• Knoten als Kreise
• Knoten als Kreise
• Kanten als „Pfeile“
• Kanten als Verbindungen, Strecken, …
• Darstellung z.B. durch „Adjazenzlisten“
• Darstellung z.B. durch „Adjazenzlisten“
53 / 1
54 / 1
Datenstrukturen
1
2
3
4
Datenstrukturen
Gliederung
• Einführung
• abstrakte Datentypen
Darstellung mit verketteten Listen
1
2
3
2
1
3
4
3
1
2
4
4
2
3
• Listen
• Stacks und queues
• Anwendungsbeispiele
• Bäume
55 / 1
56 / 1
Datenstrukturen
Datenstrukturen
Bäume
• wir werden im Laufe der Vorlesung immer wieder auf
bestehen aus:
graphentheoretische Modelle und Fragestellungen stoßen
• einer endlichen Menge V von Knoten
• ein Knoten ist als Wurzel ausgezeichnet
• jeder Knoten hat eine endliche Menge von direkten
• insbesondere kann die nächste Datenstruktur als gerichteter Graph
aufgefasst werden
Nachfolgern (Söhnen)
• sie stellt ebenso eine Verallgemeinerung von Listen dar
• die Wurzel hat keinen direkten Vorgänger (Vater)
• jeder andere Knoten hat genau einen direkten Vorgänger
57 / 1
58 / 1
Datenstrukturen
Datenstrukturen
Sprechweisen
Bäume (rekursive Definition)
Sei T ein Baum mit Wurzel r und Unterbäumen T1 , . . . , Tk .
Ferner sei ri die Wurzel des Baumes Ti .
bestehen aus einer endlichen Menge V von Knoten, so dass gilt:
• es gibt einen ausgezeichneten Knoten r (Wurzel)
• die restlichen Knoten sind partitioniert in Teilmengen T1 , . . . , Tk , die
• ri ist i-ter Sohn von r , r ist Vater der r1 , . . . rk ,
• rj ist Bruder von ri ,
• u ist Nachfolger von ri , falls u im Unterbaum Ti liegt,
• ein Knoten ohne Nachfolger heißt Blatt,
selbst wieder Bäume sind
• r zeigt auf die Wurzeln der Teilbäume r1 , . . . , rk
• Knoten von T , die nicht Blatt sind, heißen innere Knoten,
59 / 1
60 / 1
Datenstrukturen
Datenstrukturen
Bezeichnungen
Sprechweisen
T1
5
Sei T ein Baum mit Wurzel r und Unterbäumen T1 , . . . , Tk .
Wurzel
T2
Niveau
2
0
Ferner sei ri die Wurzel des Baumes Ti .
3
7
• eine Folge von Knoten v0 , v1 , . . . , vk heißt Weg, falls vi +1 Nachfolger
linker Unterbaum 5
von
von vi ist für alle 0 ≤ i ≤ k − 1,
2
• der Weg v0 , v1 , . . . , vk hat die Länge k ,
• Tiefe(v , T ) = Länge des Weges von Knoten v zur Wurzel r ,
• Höhe(v , T ) = Länge des längsten Weges von v zu einem Blatt,
• Höhe(T ) = Höhe (Wurzel, T ).
5
1
3
Knoten
8
Kanten
Tiefe(7)=2
7
5
3 ist Vater von 2
3 ist Vater von 5
2 ist linker Sohn von 3
5 ist rechter Sohn von 3
2
8
3
Blatt
5
4
62 / 1
61 / 1
Datenstrukturen
Datenstrukturen
Darstellungen von Bäumen
In der Informatik werden Bäume stets so dargestellt, dass die Wurzel oben und
die Blätter unten liegen.
• verkettete Liste mit variabler Zeigerzahl
Wurzel
Daten
Anzahl der Söhne
Pointer zu Sohn
Bäume können aufgefasst werden als:
• spezielle gerichtete Graphen,
• verallgemeinerte Listen, in denen ein Knoten mehr als einen Nachfolger
Nachteil: variable Zeigerzahl
haben kann.
63 / 1
64 / 1
Datenstrukturen
Datenstrukturen
• verkettete Liste mit 3 Datentypen
Beispiel:
• jedes Element der Liste besteht aus 3 Teilen:
0
1
1
Datum
Indikator
1
Zeiger
2
3
6
0
4
4
0
5
1
2
0
3
0
7
7
1
• Indikator = 0: ; Datum ist eine zu speichernde Größe
• Indikator = 1: ; Datum ist ein Zeiger auf eine Liste mit
Unterbaum
0
5
0
6
• wir betrachten überwiegend Bäume mit spezieller Struktur
• etwa durch Beschränkung der Anzahl der Söhne (; einfachere
Speicherung).
65 / 1
66 / 1
Datenstrukturen
Datenstrukturen
Binäre Bäume
Lemma
Ein Baum, in dem jeder Knoten höchstens 2 Söhne hat, heißt binär.
Ein binärer Baum der Höhe h hat höchstens 2h+1 − 1 Knoten und
2h Blätter.
Darstellung binärer Bäume
Beweis: (per Induktion):
√
• h=0:
• durch 2 Felder Leftsoni und Rightsoni
• dazu ggf. Informationen über: Inhalt der Knoten, Väter, # Nachfolger in
zusätzlichen Feldern
1
2
3
4
5
6
1
2
3
4
5
6
R
4
6
-
• sei T ein Baum der Höhe h
• T hat höchstens 2 Söhne r1 und r2
• r1 , r2 sind Wurzeln von Unterbäumen der Höhe höchstens h − 1
L
2
3
5
-
• per Induktion haben T1 und T2 jeweils höchstens
• 2h − 1 Knoten und 2h−1 Blätter
• damit hat T höchstens
• 2 · (2h − 1) + 1 = 2h+1 − 1 Knoten und 2 · (2h−1 ) = 2h Blätter.
67 / 1
68 / 1
Datenstrukturen
Datenstrukturen
• per Induktion gilt:
Satz
mittlere Tiefe(T1 ) ≥ log k1 ,
Die maximale und die mittlere Tiefe eines Blattes in einem Binärbaum mit
k Blättern beträgt mindestens log k .
• die Tiefe in T ist um 1 größer als in T1 bzw. T2
• somit:
Beweis:
• haben alle Blätter die Tiefe höchstens t , so hat T höchstens 2t Blätter
• ⇒ die maximale Tiefe ist mindestens log k .
” k “
”
k1 “
2
mittlere Tiefe(T1 ) + 1 +
mittlere Tiefe(T2 ) + 1
k
k
k1
k2
≥ (log k1 + 1) + (log k2 + 1)
k
k
1
= (k1 log 2k1 + k2 log 2k2 )
k
:= f (k1 , k2 )
mittlere Tiefe(T ) =
Beweis des zweiten Teils per Induktion:
√
• k =1:
.
• sei T ein Baum mit k ≥ 2 Blättern
• seien T1 , T2 die Teilbäume unter der Wurzel mit k1 bzw. k2 Blättern
• per Induktion gilt:
mittlere Tiefe(T1 ) ≥ log k1 ,
mittlere Tiefe(T2 ) ≥ log k2
Die Funktion f (k1 , k2 ) nimmt ihr Minimum unter der Bedingung
k1 + k2 = k im Punkt k1 = k2 = k2 an. Somit:
mittlere Tiefe(T2 ) ≥ log k2
mittlere Tiefe(T ) ≥
1 k
k
k
k
( log 2 + log 2 ) = log k .
k 2
2
2
2
70 / 1
69 / 1
Datenstrukturen
Datenstrukturen
Definition
Definition
Ein binärer Baum der Höhe h heißt voll, wenn er 2h+1 − 1 Knoten hat.
Ein binärer Baum mit n Knoten heißt vollständig, wenn seine Knoten den
ersten n Knoten eines sequentiell dargestellten vollen binären Baumes
entsprechen.
Sequentielle Darstellung
Die Knoten des vollen binären Baums werden beginnend in der Wurzel auf
jedem Tiefenniveau von links nach rechts durchnumeriert.
1
2
3
1
4
2
4
5
6
3
5
6
7
71 / 1
72 / 1
Datenstrukturen
Datenstrukturen
Lemma
Kompakte Darstellung vollständiger binärer Bäume
Für einen vollständigen binären Baum T mit n Knoten gilt
Vollständige binäre Bäume haben eine kompakte sequentielle Darstellung
der Relationen Vater, linker Sohn und rechter Sohn (ohne Zeiger):
Höhe(T ) = dlog(n + 1)e − 1.

bi /2c : für i > 1
− : für i = 1 (Wurzel)

2i : für 2i ≤ n
− : für 2i > n

2i + 1 : für 2i + 1 ≤ n
− : für 2i + 1 > n
Vater (i ) =
Beweis (per Induktion):
• sei h die Höhe von T
• die Knotenanzahl von T liegt zwischen denen der vollen Bäume der
Leftson (i ) =
Höhe h und h − 1
• ⇒ 2h − 1 < n ≤ 2h+1 − 1
• ⇒ h < log(n + 1) ≤ h + 1
• ⇒ h < dlog(n + 1)e ≤ h + 1
Rightson (i ) =
(da h ∈ N)
• ⇒ h + 1 = dlog(n + 1)e.
73 / 1
74 / 1
Datenstrukturen
Datenstrukturen

bi /2c : für i > 1
− : für i = 1 (Wurzel)
• diese Numerierung ist eine spezielle Aufzählung der Knoten eines

2i : für 2i ≤ n
− : für 2i > n
• in einigen Anwendungen müssen wir die Knoten eines Baumes

2i + 1 : für 2i + 1 ≤ n
− : für 2i + 1 > n
Vater (i ) =
Leftson (i ) =
Rightson (i ) =
Baums
systematisch durchsuchen:
• zum Ausgeben der Einträge
• zum Aufsummieren der Einträge
• zur Bildung eines Mittelwerts über die Einträge,. . .
1
• wir wollen somit die Knoten in einer bestimmten Reihenfolge
2
durchlaufen
3
• die gebräuchlichsten Reihenfolgen:
4
5
6
7
• Präordnung
• Postordnung
• symmetrisches Durchmustern (nur für Binärbäume)
75 / 1
76 / 1
Datenstrukturen
Datenstrukturen
Sei T ein Baum mit Wurzel r und Söhnen r1 , . . . , rk .
Eigenschaften der Durchmusterungen
Präordnung
Wir identifizieren Knoten mit ihrer Nummer.
(1) Durchmustere die Wurzel
Präordnung
(2) Durchmustere in Präordnung nacheinander T1 , . . . , Tk
(1) Durchmustere die Wurzel
(2) Durchmustere in Präordnung nacheinander T1 , . . . , Tk
Postordnung
Sei r die Wurzel des Teilbaums Tr . Dann ist
(1) Durchmustere in Postordnung nacheinander T1 , . . . , Tk
v ∈ Tr ⇐⇒ r ≤ v ≤ r + |Tr | − 1.
(2) Durchmustere die Wurzel
symmetrisches Durchmustern von Binärbäumen
Folgerung
(1) Durchmustere in symmetrischer Ordnung Tlinks (falls er existiert)
Ist |Tr | bekannt, so kann in O(1) Schritten entschieden werden, ob
v Nachfolger von r ist.
(2) Durchmustere die Wurzel
(3) Durchmustere in symmetrischer Ordnung Trechts (falls er existiert)
78 / 1
77 / 1
Datenstrukturen
Datenstrukturen
Postordnung
symmetrisches Durchmustern von Binärbäumen
(1) Durchmustere in Postordnung nacheinander T1 , . . . , Tk
(1) Durchmustere in symmetrischer Ordnung Tlinks (falls er existiert)
(2) Durchmustere die Wurzel
(2) Durchmustere die Wurzel
(3) Durchmustere in symmetrischer Ordnung Trechts (falls er existiert)
Sei r die Wurzel des Teilbaums Tr . Dann ist
v ∈ Tr ⇐⇒ r − |Tr | + 1 ≤ v ≤ r .
Die Knoten im linken Teilbaum von r tragen kleinere Nummern als r , die im
rechten größere.
Folgerung
Folgerung
Ist |Tr | bekannt, so kann in O(1) Schritten entschieden werden, ob
v Nachfolger von r ist.
Wir können (auch nach dem Löschen von Knoten) in O(Höhe(T ))
entscheiden, ob ein Knoten mit der Nummer p vorkommt.
79 / 1
80 / 1
Datenstrukturen
Datenstrukturen
Nichtrekursiver Algorithmus zur symmetrischen Durchmusterung
begin
count = 1
v = root
Top = 0
left:
while (leftson(v) 6= leer) then
push (Stack, v)
v = leftson(v)
end while
Center: Number(v) = count
count = count + 1
if (rightson(v) 6= leer) then
v = rightson(v)
goto left
end if
if (top 6= 0) then
Pop(Stack, v)
goto center
end if
end
Rekursiver Algorithmus zur symmetrischen Durchmusterung
begin
count = 1
SymmOrd(Wurzel)
end
//Hauptprogramm
procedure SymmOrd(v)
if (leftson(v) 6= leer) then SymmOrd(leftson(v))
Number(v) = count
count = count + 1
if (rightson(v) 6= leer) then SymmOrd(rightson(v))
end
81 / 1
82 / 1
Datenstrukturen
Datenstrukturen
Durch Einfügen und Löschen von Stichworten kann der Suchbaum wie folgt
aussehen:
Stichwortsuche
• gegeben: ein Lexikon mit n Stichworten
• Frage: ist ein gegebener Begriff im Lexikon enthalten?
g
a
b
d
Zwei möglich Ansätze:
i
c
d
• verkettete Listen: ; O(n) im worst-case
b
f
h
j
• binärer Baum als Suchbaum organisiert:
j
a
c
e
• Wörter im linken Unterbaum sind alphabetisch größer als das
Wort in der Wurzel
Wir werden versuchen, diese Situation zu verhindern, und balancierte Bäume verwenden.
• Wörter im rechten Unterbaum sind alphabetisch kleiner als das
Wort in der Wurzel
Wir verschieben daher die Laufzeituntersuchungen der Operationen
Folgerung
Füge_ein, Entferne, Suche und Zugriff auf Bäumen auf ein späte-
Wir können in O(Höhe(T )) entscheiden, ob das gesuchte Wort im Lexikon
vorkommt.
res Kapitel.
83 / 1
84 / 1
Herunterladen