Algorithmen und Datenstrukturen

Werbung
Was bisher geschah
ADT Menge mit Operationen:
I
Suche nach einem Element
I
Einfügen eines Elementes
I
Löschen eines Elementes
Realisierung durch verschiedene Datenstrukturen:
I
lineare Datenstrukturen:
Array, Liste, Hashtabelle
I
hierarchische Datenstrukturen:
Suchbäume, balancierte Suchbäume
Datenstrukturen mit Zugriff nur auf ein spezielles Element:
Stack, Queue mit
1. Ansehen (top, first)
2. Entfernen (pop, dequeue)
3. Einfügen einen Elementes (push, enqueue)
182
Priority-Queues
(Vorrang-Warteschlange)
Idee:
I
Modellierung von Prioritäten, z.B. Dringlichkeit
I
Datenstruktur zur Verwaltung von Paaren
(Schlüssel, Element)
I
Schlüssel repräsentieren Priorität
I
oft Rangfolge: kleiner Schlüssel = hohe Priorität
I
Zugriff nur auf ein Element mit minimalem Schlüssel
(maximale Priorität)
183
Beispiele
I
Notfallaufnahme
Prioritäten: lebensbedrohliche (1), schwere (2), leichte (3)
Verletzungen
I
Verkauf von EM-Karten
Prioritäten: Sportler (1-1000), Sponsoren (1000-2000),
Sportlerfamilien (2000-3000), Prominenz (3000-4000), . . .
I
Betriebssysteme: Scheduling von Prozessen
I
kürzeste Wege in Graphen (Dijkstra)
I
Expansionsreihenfolge der Knoten in Spielbäumen
184
ADT Priority-Queue
Menge von Schlüsseln (Prioritäten) mit einer totalen Ordnung
Sorten: Bool, Element, PQ (kurz für PQhElementi)
Signatur:
isEmpty :
PQ →
emptyPQ :
add :
PQ × Element →
getmin :
PQ →
deletemin :
PQ →
Bool
PQ
PQ
Element
PQ
Axiome (Ausschnitt): (überall ∀p, q ∈ PQ ∀e, f ∈ Element :)

isEmpty(emptyPQ) = t, isEmpty(add(p, e)) = f,




 (isEmpty(p) ∨ e ≤ getmin(p)) ↔ getmin(add(p, e)) = e,
(isEmpty(p) ∨ e ≤ deletemin(p)) ↔ getmin(add(p, e)) = p,
Φ=


e
> getmin(p) → getmin(add(p, e)) = getmin(p),



e > getmin(p) → deletemin(add(p, e)) = add(detetemin(p), e)











(+ Axiome der Booleschen Algebra)
nur für Spezifikationen:
K (p) = Menge aller Schlüssel in p
185
Wiederholung: Stack, Queue
Datenstruktur (DS) mit get / remove nur für spezielles Element:
Sorten: Bool, Element, DS (kurz für DShElementi)
Signatur:
isEmpty :
DS →
emptyDS :
add :
DS × Element →
get0 :
DS →
DS →
remove0 :
Bool
DS
DS
Element
DS
Spezialfälle:
Stack mit anderen Namen für Operationen:
add 7→ push, remove’ 7→ pop, get’ 7→ top
spezielles = „neuestes“ enthaltenes Element
Queue mit anderen Namen für Operationen:
add 7→ enqueue, remove’ 7→ dequeue, get’ 7→ front
spezielles = „ältestes“ enthaltenes Element
Priority-Queue mit anderen Namen für Operationen:
add 7→ add, remove’ 7→ deletemin, get’ 7→ getmin
spezielles = minimales enthaltenes Element
186
Stack und Queue als Priority-Queues
Queue Priorität jedes neu in die PQ eingefügten
Elementes kleiner (Schlüssel größer) als
Maximum aller schon in der PQ enthaltenen
Elemente
(Einfügen aufsteigend geordneter Schlüsselfolge)
Stack Priorität jedes neu in die PQ eingefügten
Elementes größer (Schlüssel kleiner) als
Maximum aller schon in der PQ enthaltenen
Elemente
(Einfügen absteigend geordneter Schlüsselfolge)
187
Sortieren mit Priority-Queue
Wiederholung: Spezifikation des Sortier-Problemes
V: Eingabe (x1 , x2 , . . . , xn ) mit ∀i ∈ {1, . . . , n} : xi ∈
N
N: Ausgabe (y1 , y2 , . . . , yn ) ist
1. y1 < y2 ≤ · · · < yn+1 (aufsteigend geordnet) und
2. eine Permutation (Umordnung) der Eingabe
(x1 , x2 , . . . , xn )
Algorithmus : PQ-Sort
Eingabe : x = (x1 , . . . , xn )
Ausgabe : y = (y1 , . . . , yn )
p ← emptyPQ
für jedes i ← i, . . . , n :
p ← add(p, xi )
für jedes i ← i, . . . , n :
yi ← getmin(p)
p ← deletemin(p)
Laufzeit: n-mal add + n-mal (getmin + deletemin)
abhängig von Realisierung der PQ
188
PQ – Realisierung durch Folgen
unsortierte Folge:
I
add: (als erstes / letztes Element)
O(1) bis O(n) (Liste, Array)
I
getmin, deletemin: (Minimum-Suche) O(n)
I
PQ-Sortierverfahren: O(n2 )
sortierte Folge:
I
add: (aufsteigend) sortiertes Einfügen in O(n)
I
getmin: (erstes / letztes Element) O(1)
I
deletemin: (erstes / letztes Element)
O(1) bis O(n) (Liste, Array)
I
PQ-Sortierverfahren: O(n2 )
189
PQ – Realisierung durch Bäume
Binäre Suchbäume:
Laufzeiten: Tiefe des Baumes
I
add: sortiertes Einfügen in O(log(n)) bis O(n)
I
getmin, deletemin: (linker äußerer Knoten)
O(log(n)) bis O(n)
I
PQ-Sortierverfahren: O(n log(n)) bis O(n2 )
Suchbäume mit geeigneter Balance-Eigenschaft
(z.B. AVL-Bäume, B-Bäume, Rot-Schwarz-Bäume):
Laufzeiten: Tiefe des Baumes
I
add: sortiertes Einfügen in O(log n)
I
getmin, deletemin: O(log n)
I
PQ-Sortierverfahren: O(n log(n))
190
Binäre Heaps
Ein Binärbaum t erfüllt die Heap-Eigenschaft gdw. t erfüllt
beide folgende Eigenschaften
Struktureigenschaft (Balance):
t ist ein balancierter Binärbaum in spezieller
Heap-Form:
Blatt-Schicht „von links vollständig“ belegt
Ordnungseigenschaft: t ist Heap-geordnet, d.h.
in jedem Knoten
Branch(x, Branch(y , l, r ), Branch(z, s, t)) in t gilt:
x ≤ y und x ≤ z
Beispiele (Tafel)
Auf jedem Pfad eines Heap-geordneten Baumes sind die
Schlüssel sortiert (aufsteigend von Wurzel zu Blatt)
191
Binärer Heap – add
Spezifikation add
V: Eingabe binärer Heap t, Schlüssel k
N: Ausgabe binärer Heap s mit K (s) = K (t) ∪ {k }
Idee zum Einfügen eines Schlüssels k in t:
1. Neuen Knoten mit Schlüssel k auf der ersten freien
Position im Heap t (Blattebene) einfügen
(ggf. neue Blattebene beginnen)
Heap-Ordnung ist danach evtl. verletzt
2. rekursiv entlang des Pfades vom neu eingefügten Knotens
zur Wurzel:
solange Schlüssel des Kindes größer als Schlüssel des
Vaters: Schlüssel austauschen (bubble)
Warum erhält diese Operation die Heap-Eigenschaft?
Laufzeit: O(log n) (Tiefe des Baumes)
192
Binärer Heap – getmin
Spezifikation getmin
V: Eingabe (nichtleerer) binären Heap t
N: Ausgabe Schlüssel k = min(K (t))
(d.h. ∀k 0 ∈ K (t) : k < k 0 )
Idee: Rückgabe des Schlüssels in der Wurzel des Heap
k ← getmin(Branch(k , l, r ))
Laufzeit: O(1)
193
Binärer Heap – deletemin
Spezifikation deletemin
V: Eingabe (nichtleerer) Heap t
N: Ausgabe binärer Heap s mit K (s) = K (t) \ {min(K (t))}
Idee:
Löschen des Wurzelknotens im Heap t in zwei Schritten
1. Kopie des Schlüssels des „letzen“ Blattes in die Wurzel,
Löschen des letzten Blattes
Heap-Eigenschaft ist danach evtl. verletzt
2. rekursiv beginnend in der Wurzel,
solange Schlüssel des Vaters kleiner als Schlüssel eines
Kindes:
Austausch der Schlüssel von Vater und dem Kind mit dem
kleinstem Schlüssel
Warum erhält diese Operation die Heap-Eigenschaft?
Laufzeit: O(log n) (Tiefe des Baumes)
194
Heapsort
(PQ-Sort mit PQ-Realisierung als binärer Heap)
Spezifikation Sortieren (aufsteigend):
V: Eingabe (x1 , x2 , . . . , xn ) mit ∀i ∈ {1, . . . , n} : xi ∈
N
N: Ausgabe (y1 , y2 , . . . , yn ) ist
1. y1 ≤ y2 ≤ · · · ≤ yn+1 (aufsteigend geordnet) und
2. eine Permutation (Umordnung) der Eingabe
(x1 , x2 , . . . , xn )
Algorithmus : Heapsort
Eingabe : x = (x1 , . . . , xn )
Ausgabe : y = (y1 , . . . , yn )
p ← ⊥ (leerer binärer Heap)
für jedes i ← i, . . . , n :
p ← add(p, xi )
für jedes i ← i, . . . , n :
yi ← getmin(p)
p ← deletemin(p)
Gesamtlaufzeit: O(n log n)
195
Repräsetation binärer Heaps als Arrays
Funktion t 7→ level-order(t) ist eine
Bijektion zwischen binären Heaps und Arrays
(Umkehrfunktion?)
Repräsentation eines binären Heaps t als
Array (x1 , . . . , xn ) = level-order(t)
Nachbarn des Knotens xi in x
I
Kinder: x2i , x2i+1
(in O(1))
I
Vater: xbi/2c
(in O(1))
Heapsort-Implementierung häufig mit Array-Repräsentation
(in-situ)
196
Durchquerung von Bäumen mit Priority-Queue
Wiederholung: allgemeiner Durchquerungs-Algorithmus
1. La = {s} (Wurzel s des zu durchquerenden Baumes)
2. solange La 6= ∅ wiederhole:
Auswahl und Entfernen eines Knotens u aus La
Einfügen aller Kinder von u in La
Verwaltung von La als Stack: Tiefensuche
Verwaltung von La als Queue: Breitensuche
Verwaltung von La als Priority-Queue:
Auswahl des Knotens aus La : getmin (deletemin)
Einfügen der Kinder in La : add (mit geeigneten Prioritäten)
häufige Anwendung: Priorität = Bewertung der Knoten (Heuristik)
heuristische Suchverfahren, z.B. in Spielbäumen, Labyrinth-Suche
(mehr dazu in LV Wissensverarbeitung)
197
Herunterladen