Datenstrukturen - sven.köppel.org

Werbung
Datenstrukturen
Sommersemester 2010
Isolde Adler
Herzlich willkommen!
Wiederholung
Kapitel 3: Elementare Datenstrukturen
Ankündigung
Vortrag von Prof. Douglas Rogers:
Zero-one evaluations for the classic non-associative bracketing problem
• Fragestellungen aus der Kombinatorik
• u.A. die berühmten Catalan-Zahlen
• historische Zusammenhänge
• Wann? Kommenden Dienstag, 11.5.2010, 10:00 c.t. hier im
Magnus-Hörsaal
Sie sind herzlich eingeladen!
Isolde Adler
Datenstrukturen 2010
2/30
Graphen
Wiederholung
Kapitel 3: Elementare Datenstrukturen
Zusätzliche Übungsgruppe
Wir konnten noch eine zusätzliche Übungsgruppe einrichten!
• Mittwochs, 8-10 Uhr. Nächster Termin: 12.5.2010
• in Raum NM 125
• Leitung: Herr Maziar Behdju
• Sie können gerne noch zu Herrn Behdju wechseln!
Isolde Adler
Datenstrukturen 2010
3/30
Graphen
Wiederholung
Kapitel 3: Elementare Datenstrukturen
Graphen
Anwesenheit in den Übungen
• Die Teilnahme an den Übungen ist freiwillig, aber . . .
nehmen Sie unbedingt an den Übungen teil!!
Es sind insgesamt nur 7 Termine!
• Bearbeiten Sie unbedingt die Aufgabenblätter!!
• Erfahrungsgemäß entscheidet das in fast allen Fällen über das
Bestehen der Klausur
• Wenn Sie sich entscheiden würden, an einem kostenpflichtigen
Intensivkurs teilzunehmen, würden Sie auch versuchen, jeden Termin
wahrzunehmen
• 10% der Punkte aus den Übungen können bei regelmäßiger und
aktiver Teilnehme an den Übungen mit in die Klausur genommen
werden
Isolde Adler
Datenstrukturen 2010
4/30
Wiederholung
Kapitel 3: Elementare Datenstrukturen
Graphen
Zusammenfassung der letzten Vorlesungsstunde, 1
Analyse von C++ Programmen
• Zuweisungen: Eine Zuweisung zu einer einfachen“ Variablen ist
”
einfach zu zählen, eine Zuweisung zu einer Array-Variablen ist mit
der Länge des Arrays zu gewichten.
• Auswahl-Anweisungen: Häufig genügt: Bedingung + Gesamtaufwand
für den längsten der alternativen Anweisungsblöcke.
• Schleifen: Häufig genügt: Maximale Anzahl der auszuführenden
Anweisungen innerhalb einer Schleife × Anzahl der
Schleifendurchläufe
• Beispiel:
Matrizenmultiplikation A · B = C
for (i=0; i < n; i++)
for (j=0; j < n; j++) {
C[i][j] = 0;
for (k=0; k < n ; k++)
C[i][j] += A[i][k]*B[k][j];
}
Isolde Adler
Datenstrukturen 2010
5/30
Wiederholung
Kapitel 3: Elementare Datenstrukturen
Graphen
Zusammenfassung der letzten Vorlesungsstunde, 2
Datenstrukturen:
• Einfach verkettete Listen,
Anwendungsbeispiel: Addition dünnbesetzter Matrizen
• Stacks: last in first out (LIFO)
• Queues: first in first out (FIFO)
• Deques: kombinieren Stacks und Queues
• Bäume als Datenstrukturen
Isolde Adler
Datenstrukturen 2010
6/30
Wiederholung
Kapitel 3: Elementare Datenstrukturen
Ausblick:
• verschiedene Baum-Implementierungen
• Suche in Bäumen, Preorder, Postorder, Inorder
• Graphen: Grundbegriffe und topologisches Sortieren
Isolde Adler
Datenstrukturen 2010
7/30
Graphen
Wiederholung
Kapitel 3: Elementare Datenstrukturen
Graphen
Operationen auf Bäumen (als Datenstrukturen)
(1) Wurzel: Bestimme die Wurzel von T .
(2) Vater(v ): Bestimme den Vater des Knoten v in T .
Wenn v = r , dann ist der Null-Zeiger auszugeben.
(3) Kinder(v ): Bestimme die Kinder von v . Wenn v ein Blatt ist, dann
ist der Null-Zeiger als Antwort zu geben.
(4) Für binäre geordnete Bäume:
(4a) LKind(v ): Bestimme das linke Kind von v .
(4b) RKind(v ): Bestimme das rechte Kind von v .
(4c) Sollte das entsprechende Kind nicht existieren, ist der Null-Zeiger als
Antwort zu geben.
(5) Tiefe(v ): Bestimme die Tiefe von v .
(6) Höhe(v ): Bestimme die Höhe von v .
(7) Baum(v , T1 , . . . , Tm ): Erzeuge einen geordneten Baum mit Wurzel v
und Teilbäumen T1 , . . . , Tm .
(8) Suche(x): Bestimme alle Knoten mit Wert x.
Isolde Adler
Datenstrukturen 2010
8/30
Wiederholung
Kapitel 3: Elementare Datenstrukturen
Implementierung mittels Vater-Array
Annahme: Jeder Knoten besitzt eine Zahl aus {1, . . . , n} als Namen und
zu jedem i ∈ {1, . . . , n} gibt es genau einen Knoten mit Namen i.
• Ein Integer-Array Parent speichert für jeden Knoten v den Namen
des Vaters von v :
Parent[v ] = Name des Vaters von v .
Wenn v = r , dann setze Parent[v ] = r .
• Das Positive:
+ schnelle Vater-Bestimmung (Zeit = O(1))
+ und schnelle Bestimmung der Tiefe von v (Zeit = O(Tiefe(v ))).
+ Minimaler Speicherplatzverbrauch: Bäume mit n Knoten benötigen
Speicherplatz n.
• Das Negative: für die Bestimmung der Kinder muss der gesamte
Baum durchsucht werden (Zeit = O(Anzahl Knoten).)
Isolde Adler
Datenstrukturen 2010
9/30
Graphen
Wiederholung
Kapitel 3: Elementare Datenstrukturen
Die Adjazenzlisten-Implementierung
Für Bäume mit n Knoten benutze ein Kopf-Array“ Head mit den Zellen
”
1, . . . , n: Head[i] ist ein Zeiger auf die Liste der Kinder von i.
1
2
3
4
5
6
7
8
9
10
Isolde Adler
- 10
-1
-2
-4
-7
-9
-
Datenstrukturen 2010
-3
-
-6
-8
-
-
10/30
Graphen
Wiederholung
Kapitel 3: Elementare Datenstrukturen
Graphen
Stärken und Schwächen
+ Die Kinderbestimmung gelingt schnellstmöglich, in Zeit proportional
zur Anzahl der Kinder.
+ Höhe (v , T ) wird auch angemessen unterstützt mit der Laufzeit
O(Anzahl der Knoten im Teilbaum mit Wurzel v ). Warum?
- Für die Bestimmung des Vaters muss möglicherweise der gesamte
Baum durchsucht werden! Auch die Bestimmung der Tiefe ist
schwierig, da der Vater nicht bekannt ist.
- Speicherplatzverbrauch: Für Bäume mit n Knoten benötigen wir
• 2n − 1 Zeiger: einen Zeiger für jede der n Zellen von Head und einen
Zeiger für jede der n − 1 Kanten
• und 2n − 1 Zellen: n Zellen für das Array Head und n − 1 Zellen für
die n − 1 Kanten.
Zu großer Speicherplatz. Aber die Adjazenzlisten-Darstellung kann
auch für Graphen benutzt werden.
Isolde Adler
Datenstrukturen 2010
11/30
Wiederholung
Kapitel 3: Elementare Datenstrukturen
Die Binärbaum-Implementierung
Ein Knoten wird durch die Struktur
typedef struct Knoten {
int wert;
Knoten *links, *rechts; };
dargestellt.
• Wenn der Zeiger z auf die Struktur des Knoten v zeigt,
• dann ist z → wert der Wert von v und
• z → links (bzw. z → rechts) zeigt auf die Struktur des linken
(bzw. rechten) Kindes von v .
• Der Zeiger wurzel zeige auf die Struktur der Wurzel des Baums.
• Im Vergleich zur Adjazenzlisten-Darstellung:
• Ähnliches Laufzeitverhalten bei den Operationen
• aber bessere Speichereffizienz: 2n Zeiger (zwei Zeiger pro Knoten)
und n Zellen (eine Zelle pro Knoten).
Isolde Adler
Datenstrukturen 2010
12/30
Graphen
Wiederholung
Kapitel 3: Elementare Datenstrukturen
Die Kind-Geschwister-Implementierung
Ein Knoten wird durch die Struktur
typedef struct Knoten {
int wert;
Knoten *LKind, *RGeschwister; };
dargestellt.
• Wenn der Zeiger z auf die Struktur des Knoten v zeigt,
• dann ist z -> wert der Wert von v und
• z -> LKind (bzw. z -> RGeschwister) zeigt auf die Struktur des
linken Kindes, bzw. des rechten Geschwisterknoten von v .
• Der Zeiger wurzel zeige wieder auf die Struktur der Wurzel des
Baums.
• Im Vergleich zur Binärbaum-Darstellung:
• Ähnliches Laufzeitverhalten und ähnliche Speichereffizienz,
• aber die Darstellung ist für alle Bäume und nicht nur Binärbäume
anwendbar!
Isolde Adler
Datenstrukturen 2010
13/30
Graphen
Wiederholung
Kapitel 3: Elementare Datenstrukturen
Graphen
Suche in Bäumen: Postorder, Preorder und Inorder
Sei T ein geordneter Baum mit Wurzel r und Teilbäumen T1 , . . . , Tm .
• Postorder: Durchlaufe rekursiv die Teilbäume
T1 , . . . , Tm nacheinander. Danach wird die Wurzel r besucht.
• Preorder: besuche zuerst r und danach durchlaufe rekursiv die
Teilbäume T1 , . . . , Tm .
• Inorder: Durchlaufe zuerst T1 rekursiv, sodann die Wurzel r und
letztlich die Teilbäume T2 , . . . , Tm rekursiv.
void preorder (Knoten *p){
Knoten *q;
if (p != null) {
cout << p->wert;
for (q=p->LKind; q!=null; q = q->RGeschwister)
preorder(q); }}
Isolde Adler
Datenstrukturen 2010
14/30
Wiederholung
Kapitel 3: Elementare Datenstrukturen
Bestimmung der Tiefe und Höhe von Knoten
Die Struktur eines Knoten besteht aus den Feldern tiefe, hoehe, LKind
und RGeschwister.
• void tiefe (Knoten *p, int t) {
Knoten *q;
p->tiefe = t; t = t+1;
for(q = p->LKind; q!=null; q = q->RGeschwister)
tiefe(q,t); }
• Aufruf mit if (wurzel != null) tiefe(wurzel,0);
• int hoehe (Knoten *p){
Knoten *q; int h=-1;
for(q = p->LKind; q!=null; q = q->RGeschwister)
h = max(h,hoehe(q));
h=h+1;
p->hoehe = h; return h; }
• Aufruf mit if (p != null) hoehe(p);
Isolde Adler
Datenstrukturen 2010
15/30
Graphen
Wiederholung
Kapitel 3: Elementare Datenstrukturen
Welcher Knoten wird direkt nach v besucht?
Postorder:
• das linkeste Blatt im rechten Nachbarbaum.
• Wenn v keinen rechten Geschwisterknoten besitzt, dann wird der
Vater von v als nächster besucht.
Preorder:
• das linkeste Kind von v .
• Wenn v ein Blatt ist, dann das erste nicht-besuchte Kind des
tiefsten, nicht vollständig durchsuchten Vorfahren von v
Inorder:. . . ?
Wenn wir zusätzliche Zeiger gemäß einer dieser Reihenfolgen einsetzen,
dann können wir Rekursion vermeiden: besuche die Knoten gemäß der
neuen Zeiger (zusätzlicher Verwaltungsaufwand).
Die Datenstrukturen heißen dann postorder-, preorder-, inorder-gefädelte
Bäume
Isolde Adler
Datenstrukturen 2010
16/30
Graphen
Wiederholung
Kapitel 3: Elementare Datenstrukturen
Graphen
Eine nicht-rekursive Preorder-Implementierung
Der Teilbaum mit Wurzel v ist in Preorder-Reihenfolge zu durchlaufen.
(1) Wir fügen einen Zeiger auf die Struktur von v in einen anfänglich
leeren Stack ein.
(2) Solange der Stack nicht leer ist, wiederhole:
(a) Entferne das erste Stack-Element w mit Hilfe der Pop-Operation.
Besuche w .
(b) Füge die Kinder von w in umgekehrter Reihenfolge in den Stack ein.
/∗ Durch die Umkehrung der Reihenfolge werden die Bäume später
in ihrer natürlichen Reihenfolge abgearbeitet. ∗ /
Die Laufzeit ist linear in der Knotenzahl n.
• Jeder Knoten wird genau einmal in den Stack eingefügt.
• Insgesamt werden also höchstens O(n) Stackoperationen
durchgeführt. Stackoperationen dominieren aber die Laufzeit.
Isolde Adler
Datenstrukturen 2010
17/30
Wiederholung
Kapitel 3: Elementare Datenstrukturen
Laufzeit
Für einen Baum mit n Knoten, der in Kind-Geschwister-Darstellung,
Adjazenzisten- oder Binärbaum-Darstellung vorliegt, können die
Reihenfolgen Postorder, Präoerder bzw. Inorder in Zeit O(n) berechnet
werden.
Isolde Adler
Datenstrukturen 2010
18/30
Graphen
Wiederholung
Kapitel 3: Elementare Datenstrukturen
Graphen
(a) Ein ungerichteter Graph G = (V , E ) besteht aus einer endlichen
Menge
und einer Teilmenge
V von Knoten (engl.: vertices)
E ⊆ {u, v } u, v ∈ V , u 6= v von Kanten (engl.: edges).
• Die Endpunkte u, v einer ungerichteten Kante {u, v } sind
gleichberechtigt.
• u und v heißen dann Nachbarn.
Wir sagen auch: u und v sind adjazent.
(b) Für die Kantenmenge E eines gerichteten Graphen G = (V , E ) gilt
E ⊆ {(u, v ) | u, v ∈ V , u 6= v }.
• Der Knoten u ist Anfangspunkt und der Knoten v Endpunkt der
Kante (u, v ).
• v heißt auch ein direkter Nachfolger von u und u ein direkter
Vorgänger von v .
Isolde Adler
Datenstrukturen 2010
19/30
Graphen
Wiederholung
Kapitel 3: Elementare Datenstrukturen
Warum Graphen?
Graphen modellieren
• das World Wide Web: Die Knoten entsprechen Webseiten, die
(gerichteten) Kanten entsprechen Hyperlinks.
• Rechnernetzwerke: Die Knoten entsprechen Rechnern, die
(gerichteten und/oder ungerichteten) Kanten entsprechen
Direktverbindungen zwischen Rechnern.
• Das Schienennetz der Deutschen Bahn: Die Knoten entsprechen
Bahnhöfen, die (ungerichteten) Kanten entsprechen
Direktverbindungen zwischen Bahnhöfen.
Bei der Erstellung von Reiseplänen müssen kürzeste (gewichtete)
Wege zwischen einem Start- und einem Zielbahnhof bestimmt
werden.
• Schaltungen: die Knoten entsprechen Gattern, die (gerichteten)
Kanten entsprechen Leiterbahnen zwischen Gattern.
Isolde Adler
Datenstrukturen 2010
20/30
Graphen
Wiederholung
Kapitel 3: Elementare Datenstrukturen
Graphen
Das Königsberger Brückenproblem
Gibt es einen Rundweg durch Königsberg, der alle Brücken über den
Pregel genau einmal überquert?
1
BBBBBBB
BBBBBBB
BBBBBBB
2
BBBBBBB
BBBBBBB
BBBBBBB
3
4
Der zugehörige ungerichtete Graph (mit Mehrfachkanten!):
#
1j
!
2j
"
4j
Isolde Adler
Datenstrukturen 2010
3j
!
21/30
Wiederholung
Kapitel 3: Elementare Datenstrukturen
Graphen
Euler-Kreise
Ein Euler-Kreis beginnt in einem Knoten v , durchläuft alle Kanten genau
einmal und kehrt dann zu v zurück.
Das Königsberger Brückenproblem besitzt
1f
2f 4f
keine Lösung, denn der Graph
3f
hat keinen Euler-Kreis: Ansonsten hätte jeder Knoten eine gerade Anzahl
von Nachbarn! Und der folgende Graph?
h
0 @
@
Rh
1h
2
PP
1
6PP
q ?
3h
4h
I
@
@ 5h
Isolde Adler
Datenstrukturen 2010
22/30
Wiederholung
Kapitel 3: Elementare Datenstrukturen
Wichtige Begriffe
Sei G = (V , E ) ein gerichteter oder ungerichteter Graph.
• Eine Folge (v0 , v1 , ..., vm ) heißt ein Weg in G ,
falls für jedes i (0 ≤ i < m) gilt
• (vi , vi+1 ) ∈ E (für gerichtete Graphen) bzw.
• {vi , vi+1 } ∈ E (für ungerichtete Graphen).
Die Weglänge ist m, die Anzahl der Kanten. Ein Weg heißt einfach,
wenn kein Knoten zweimal auftritt.
• Ein Weg heißt ein Kreis, wenn v0 = vm und (v0 , ...vm−1 ) ein
einfacher Weg ist. G heißt azyklisch, wenn G keine Kreise hat.
• Ein ungerichteter Graph heißt zusammenhängend, wenn je zwei
Knoten durch einen Weg miteinander verbunden sind.
Isolde Adler
Datenstrukturen 2010
23/30
Graphen
Wiederholung
Kapitel 3: Elementare Datenstrukturen
Topologisches Sortieren
n Aufgaben a0 , . . . , an−1 sind auszuführen. Allerdings gibt es eine Menge
P von p Prioritäten zwischen den einzelnen Aufgaben.
Die Priorität (i, j) impliziert, dass Aufgabe ai vor Aufgabe aj
ausgeführt werden muss.
Das Ziel ist die Erstellung einer Reihenfolge, in der alle Aufgaben
ausgeführt werden können, bzw. festzustellen, dass eine solche
Reihenfolge nicht existiert.
• Eine graphentheoretische Formulierung:
• Wähle V = {0, . . . , n − 1} als Knotenmenge.
Wir verabreden, dass Knoten i der Aufgabe ai entspricht.
• Wir setzen genau dann eine Kante von i nach j ein,
wenn (i, j) eine Priorität ist.
• Wie ist das Ziel zu formulieren?
Bestimme eine Reihenfolge v1 , . . . , vi , . . . , vn der Knoten, so dass es
keine Kante (vi , vj ) mit j < i gibt.
Isolde Adler
Datenstrukturen 2010
24/30
Graphen
Wiederholung
Kapitel 3: Elementare Datenstrukturen
Graphen
Die Idee
Eine Aufgabe aj kann als erste Aufgabe ausgeführt werden, wenn es keine
Priorität der Form (i, j) in P gibt.
• Ein Knoten v von G heißt eine Quelle, wenn Eingangsgrad(v ) = 0
ist, wenn v also kein Endpunkt einer Kante ist.
• Also bestimme eine Quelle v , führe v aus und entferne v .
• Wiederhole dieses Verfahren, solange G noch Knoten besitzt:
bestimme eine Quelle v , führe v aus und entferne v .
Welche Datenstrukturen sollten wir verwenden?
Isolde Adler
Datenstrukturen 2010
25/30
Wiederholung
Kapitel 3: Elementare Datenstrukturen
Graphen
Ein erster Versuch
Wir verketten alle p Kanten in einer Liste Priorität“ und benutzen ein
”
Integer-Array Reihenfolge“ sowie zwei boolesche Arrays Erster“ und
”
”
Fertig“ mit jeweils n Zellen.
”
Zaehler = 0. Für alle i setze Fertig[i] = falsch.
Wiederhole n Mal:
(0) Durchlaufe das Array Erster. Setze Erster[i] = wahr genau dann,
wenn Fertig[i] = falsch.
(1) Durchlaufe die Liste Priorität. Wenn Kante (i, j) angetroffen
wird, setze Erster[j] = falsch.
(2) Bestimme das kleinste j mit Erster[j] = wahr. Danach setze
(a) Fertig[j] = wahr,
(b) Reihenfolge[Zaehler++] = j (Aufgabe j wird ausgeführt)
(c) und durchlaufe die Prioritätsliste: entferne jede Kante (j, k), da aj
eine Ausführung von Aufgabe ak nicht mehr behindert.
Isolde Adler
Datenstrukturen 2010
26/30
Wiederholung
Kapitel 3: Elementare Datenstrukturen
Eine Laufzeitanalyse
• Was ist besonders teuer?
• In jeder Iteration muss die Liste Priorität vollständig durchlaufen
werden: Zeit = O(p).
• Weiterhin muss das Array Erster jeweils initialisiert werden:
Zeit = O(n).
• Die Laufzeit pro Iteration ist dann durch O(p + n) beschränkt. Die
Gesamtlaufzeit ist O(n · (p + n)), da wir n Iterationen haben.
• Was können wir verbessern?
• Wir müssen nur die in j beginnenden Kanten entfernen.
• Warum kompliziert nach der ersten ausführbaren Aufgabe suchen?
Eine vor aj nicht in Betracht kommende Aufgabe ak wird nur
interessant, wenn (j, k) eine Priorität ist.
Isolde Adler
Datenstrukturen 2010
27/30
Graphen
Wiederholung
Kapitel 3: Elementare Datenstrukturen
Der zweite Versuch
Stelle die Prioritäten durch eine Adjazenzliste mit dem Kopf-Array
Priorität dar. Benutze ein Array Eingangsgrad mit Eingangsgrad[v ] = k,
falls v Endpunkt von k Kanten ist.
(1) Initialisiere die Adjazenzliste Priorität durch Einlesen aller
Prioritäten. (Zeit = O(n + p)).
(2) Initialisiere das Array Eingangsgrad. (Zeit = O(n + p)).
(3) Alle Knoten v mit Eingangsgrad[v ] = 0 werden in eine Schlange
gestopft. (Zeit = O(n)).
(4) Setze Zähler = 0; Wiederhole solange, bis Schlange leer ist:
(a) Entferne einen Knoten i aus Schlange.
(b) Setze Reihenfolge [Zähler++] = i.
(c) Durchlaufe die Liste Priorität[i] und reduziere den Eingangsgrad für
jeden Nachfolger j von i um 1. Wenn jetzt Eingangsgrad[j] = 0,
dann stopfe j in Schlange: Aufgabe aj ist jetzt ausführbar.
Isolde Adler
Datenstrukturen 2010
28/30
Graphen
Wiederholung
Kapitel 3: Elementare Datenstrukturen
Graphen
Die Analyse
• Die Initialisierungen laufen in O(n + p) Schritten ab.
• Ein Knoten wird nur einmal in die Schlange eingefügt. Also
beschäftigen sich höchstens O(n) Schritte mit der Schlange.
• Eine Kante (i, k) wird, mit Ausnahme der Initialisierungen, nur dann
inspiziert, wenn i aus der Schlange entfernt wird.
• Jede Kante wird nur einmal angefasst“
”
• und höchstens O(p) Schritte behandeln Kanten.
Das Problem des topologischen Sortierens wird für einen Graphen mit n
Knoten und p Kanten in Zeit O(n + p) gelöst.
Schneller geht’s nimmer.
Isolde Adler
Datenstrukturen 2010
29/30
Wiederholung
Kapitel 3: Elementare Datenstrukturen
Graph-Implementierungen
• Welche Datenstruktur sollten wir für die Darstellung
eines Graphen G wählen?
• Welche Operationen sollen schnell ausführbar sein?
• Ist e eine Kante von G ? Die Adjazenzmatrix wird sich als eine gute
Wahl herausstellen.
• Bestimme die Nachbarn, bzw. Vorgänger und Nachfolger eines
Knoten. Die Adjazenzlistendarstellung ist unschlagbar.
Besonders die Nachbar- und Nachfolgerbestimmung ist wichtig, um
Wege zu durchlaufen.
Isolde Adler
Datenstrukturen 2010
30/30
Graphen
Herunterladen