SS11 Algorithmen und Datenstrukturen 2. Kapitel Fundamentale Datentypen und Datenstrukturen Martin Dietzfelbinger April 2011 FG KTuEA, TU Ilmenau Algorithmen und Datenstrukturen – SS11 – Kapitel 2 Einfache Datentypen • Stacks (Keller, Stapel, LIFO-Speicher) • Indizierte dynamische Folgen (Dynamische Arrays) • Queues (Warteschlangen, FIFO-Speicher) • Mengen • Wörterbücher Einfache Datenstrukturen • Statische Arrays (1. Semester) • Einfach und doppelt verkettete Listen (1. Semester) FG KTuEA, TU Ilmenau Algorithmen und Datenstrukturen – SS11 – Kapitel 2 1 2.1 Stacks (Keller, Stapel, LIFO-Speicher∗) empty push(2) 2 push(7) push(4) 4 top 7 4 2 7 2 1 isempty? top 4 2 1 2 pop push(1) 4 4 1 7 4 2 2 false pop 1 4 pop 4 pop 2 2 2 ∗ isempty? true Last-In-First-Out FG KTuEA, TU Ilmenau Algorithmen und Datenstrukturen – SS11 – Kapitel 2 1 Was sollen die Operationen bewirken? Informal: Als Stackeinträge zugelassen sind Elemente einer Menge D (Parameter). empty (()): erzeuge leeren Stack ( Konstruktor“). ” push(s, x): lege neues Element x ∈ D auf den Stack s. pop(s): entferne oberstes Element von Stack s (falls nicht vorhanden: Fehler) top(s): gib oberstes Element von Stack s aus (falls nicht vorhanden: Fehler) isempty (s): Ausgabe true“ falls Stack s leer, false“ sonst. ” ” Brauchen: Präzise Beschreibung. FG KTuEA, TU Ilmenau Algorithmen und Datenstrukturen – SS11 – Kapitel 2 2 Spezifikation des Datentyps Stack über D“ ” 1. Signatur: Sorten: Elements Stacks Boolean Operationen: empty : → Stacks push: Stacks × Elements → Stacks pop: Stacks → Stacks top: Stacks → Elements isempty : Stacks → Boolean Namen von Objekttypen, Namen von Operationen mit Angabe der Typen von Argument(en) und Resultat(en). Rein syntaktische Vorschriften! Verhalten (noch) ungeklärt! FG KTuEA, TU Ilmenau Algorithmen und Datenstrukturen – SS11 – Kapitel 2 3 Spezifikation des Datentyps Stack“ über D ” 2. Mathematisches Modell Sorten: Elements: (nichtleere) Menge D (Parameter) Stacks: D<∞ = Seq(D) wo Seq(D) = {(a1, . . . , an) | n ∈ N, a1, . . . , an ∈ D} Boolean: {true, false} bzw. {1, 0} Objekttypen werden durch Mengen modelliert. Die leere Folge ( ) stellt den leeren Stack dar. FG KTuEA, TU Ilmenau Algorithmen und Datenstrukturen – SS11 – Kapitel 2 4 Spezifikation des Datentyps Stack“ über D ” 2. Mathematisches Modell (Forts.) Operationen: empty () = () push((a1, . . . , an), x) := (x, a1, . . . , an) pop((a1, . . . , an)) := (a2, . . . , an), falls n ≥ 1 undefiniert, falls n = 0 a1, top((a1, . . . , an)) := undefiniert, false, isempty ((a1, . . . , an)) := true, falls n ≥ 1 falls n = 0 falls n ≥ 1 falls n = 0 Operationen werden durch Funktionen modelliert. FG KTuEA, TU Ilmenau Algorithmen und Datenstrukturen – SS11 – Kapitel 2 5 Spezifikation des Datentyps Stack“ über D ” Alternative: Axiome (siehe z.B. Saake/Sattler, AuD, Kap. 11) Signatur: wie oben. Axiome: ∀ s: Stacks, ∀ x: Elements pop(push(s, x)) = s top(push(s, x)) = x isempty (empty ()) = true isempty (push(s, x)) = false Vorteil: Zugänglich für automatisches Beweisen von Eigenschaften der Datenstruktur. – Nachteil: Finden eines geeigneten Axiomensystems evtl. nicht offensichtlich. FG KTuEA, TU Ilmenau Algorithmen und Datenstrukturen – SS11 – Kapitel 2 6 Implementierung von Stacks Grundsätzlich, in objektorientierter Sprache: als Klasse. Kapselung – Information Hiding Signatur = b Interface (Java) bzw. h-Datei (C++) Parameter: Typ elements. Instanzen sind die Elemente der Menge D. (Mindestens) Zwei strukturell unterschiedliche Möglichkeiten für interne Realisierung: • verkettete Liste • Array FG KTuEA, TU Ilmenau Algorithmen und Datenstrukturen – SS11 – Kapitel 2 7 Listenimplementierung von Stacks (siehe z.B. Saake/Sattler, AuD, Kap. 13.2) a1 s: a2 (Das Symbol an steht für den Nullzeiger) s.empty: Erzeuge leere Liste. s: s.push(x): Setze neuen Listenknoten mit Eintrag x an den Anfang der Liste. s: FG KTuEA, TU Ilmenau x a1 a2 Algorithmen und Datenstrukturen – SS11 – Kapitel 2 an 8 Listenimplementierung von Stacks s.pop: Entferne ersten Listenknoten (falls vorhanden) a2 s: an s.top: Gib Inhalt des ersten Listenknotens aus (falls vorhanden) s: FG KTuEA, TU Ilmenau a1 a2 Algorithmen und Datenstrukturen – SS11 – Kapitel 2 an 9 Listenimplementierung von Stacks s.isempty: Falls Liste leer: Ausgabe true, sonst false s: s: a1 Einzige Fehlermöglichkeit: Speicherüberlauf bei s.push(x). FG KTuEA, TU Ilmenau Algorithmen und Datenstrukturen – SS11 – Kapitel 2 10 Korrektheit der Listenimplementierung Wir betrachten eine Folge Op0 = empty, Op1, . . . , OpN von Stackoperationen, wobei empty nur ganz am Anfang vorkommen darf. Diese Folge erzeugt im mathematischen Modell eine Folge (s0, s1, s2, . . . , sN ) von Stacks (Tupeln) (man wende die Operationen in der offensichtlichen Weise an) und eine Folge (z0, z1, z2, . . . , zN ) von Ausgaben. FG KTuEA, TU Ilmenau Algorithmen und Datenstrukturen – SS11 – Kapitel 2 11 Beispiel: FG KTuEA, TU Ilmenau OPi empty push(2) push(4) push(7) top pop push(1) top isempty top pop pop pop isempty pop push(3) .. si () (2) (4, 2) (7, 4, 2) (7, 4, 2) (4, 2) (1, 4, 2) (1, 4, 2) (1, 4, 2) (1, 4, 2) (4, 2) (2) () () ∗ ∗ .. zi – – – – 7 – – 1 false 1 – – – true ∗ ∗ .. Algorithmen und Datenstrukturen – SS11 – Kapitel 2 12 Korrektheit der Listenimplementierung Dabei liefern Op0 = empty (), Opi = push(x) und Opi = pop die Ausgabe −“. ” Die Operationen Opi = top liefert normalerweise die erste Komponente von si−1 als Ausgabe. Fehlerfall: Wenn j minimal ist, so dass Opj = top oder Opj = pop ist, wobei sj−1 = (), dann ist sj = · · · = sN = ∗ und zj = · · · = zN = ∗. Die Operation Opi = isempty liefert true, wenn si−1 = () gilt, und false, wenn si−1 6= (). FG KTuEA, TU Ilmenau Algorithmen und Datenstrukturen – SS11 – Kapitel 2 13 Satz 2.1.1 Die Listenimplementierung ist korrekt, d. h. sie erzeugt genau dieselbe Ausgabefolge wie das mathematische Modell, solange weder im mathematischen Modell ein Fehler (top oder pop auf leerem Stack) auftritt noch bei (push(x)) in der Implementierung ein Laufzeitfehler Speicherüberlauf“ eintritt. ” Beweis: Man zeigt per Induktion über i, dass nach Ausführen der Operation Opi folgende Behauptung (IBi) gilt: Die Einträge in der Liste sind genau die Einträge in si, d. h. die Liste ist eine präzise Darstellung von si, wenn in Schritten 0 . . . , i kein Fehler aufgetreten ist. Daraus folgt unmittelbar, dass die Ausgabe bei der Implementierung mit der im mathematischen Modell übereinstimmt. FG KTuEA, TU Ilmenau Algorithmen und Datenstrukturen – SS11 – Kapitel 2 14 Zeitaufwand bei Listenimplementierung Behauptung: Bei der Listenimplementierung hat jede einzelne Operation Kosten O(1). Das ist klar. Kosteneinheit für push(x) allerdings: Allokation eines Listenknotens – Systemaufruf ( malloc“ in C) – um einiges ” teurer als Arrayzugriff. FG KTuEA, TU Ilmenau Algorithmen und Datenstrukturen – SS11 – Kapitel 2 15 Arrayimplementierung von Stacks (Vgl. Saake/Sattler, AuD, Kap. 13.1) Das Stackobjekt heißt s; es hat als Komponenten einen Zeiger/eine Referenz auf ein Array A[1..m] und eine Integervariable p ( Pegel“). ” s: p: n 1 2 A: a n n a 2 a1 * m * * * Achtung: In A[1] steht an, . . . , in A[n] steht (der oberste Stackeintrag) a1. ∗“ steht für einen beliebigen Eintrag. ” FG KTuEA, TU Ilmenau Algorithmen und Datenstrukturen – SS11 – Kapitel 2 16 Arrayimplementierung von Stacks s.empty: wird durch Konstruktor realisiert; Option: m als Parameter Erzeuge neues Objekt s mit A[1..m] of elements und p: int ; p ← 0; s.push(x): if p = A.length then Fehler“ (z.B. OverflowException) ” else p ← p+1; A[p] ← x; s.pop: if p = 0 then Fehler“ (z.B. StackemptyException) ” else p ← p-1; s.top: if p = 0 then Fehler“ (z.B. StackemptyException) ” else return A[p]; s.isempty: if p = 0 then return true“ ” else return false“; ” FG KTuEA, TU Ilmenau Algorithmen und Datenstrukturen – SS11 – Kapitel 2 17 FG KTuEA, TU Ilmenau Algorithmen und Datenstrukturen – SS11 – Kapitel 2 18 Korrektheit der Arrayimplementierung Wie vorher: Gegeben ist eine Folge Op0 = empty, Op1, . . . , OpN von Stackoperationen (wobei empty nur ganz am Anfang vorkommen darf), die im mathematischen Modell eine Folge (s0, s1, s2, . . . , sN ) von Stacks (Tupeln) und eine Folge (z0, z1, z2, . . . , zN ) von Ausgaben erzeugt. FG KTuEA, TU Ilmenau Algorithmen und Datenstrukturen – SS11 – Kapitel 2 19 Satz 2.1.2 Die Arrayimplementierung ist korrekt, d. h. sie erzeugt genau dieselbe Ausgabefolge wie das mathematische Modell, solange weder im mathematischen Modell ein Fehler (top oder pop auf leerem Stack) auftritt noch die Höhe des Stacks si die Arraygröße m überschreitet. Beweis: Man beweist durch Induktion über i = 0, 1, . . . , N , dass nach Ausführen der Operation Opi folgende Invariante gilt (falls keiner der genannten Fehler aufgetreten ist): • p enthält die Länge ni von si und • si = (A[ni], . . . , A[1]) D. h.: Der Teil A[1..ni] des Arrays stellt genau si dar (in umgekehrter Reihenfolge). FG KTuEA, TU Ilmenau Algorithmen und Datenstrukturen – SS11 – Kapitel 2 20 Beachte: Der Teil A[ni + 1..m] des Arrays kann völlig beliebige Einträge enthalten. D.h.: Ein und derselbe Stack s im Sinn des mathematischen Modells kann durch verschieden beschriftete Arrays A[1..m] dargestellt werden. I.A.: i = 0. Anfänglich hat p den Inhalt n0 = 0. ⇒ (A[ni], . . . , A[1]) ist die leere Folge (), also gleich s0. I.V.: i > 0, und die Behauptung gilt für i − 1. FG KTuEA, TU Ilmenau Algorithmen und Datenstrukturen – SS11 – Kapitel 2 21 I.S.: Es gibt verschiedene Fälle. Fall 0: si−1 = ∗ oder in Schritten 1, . . . , i − 1 ist ein Arrayüberlauf aufgetreten. Dann ist nichts zu zeigen. Ab hier ist nach I.V. si−1 = (a1, . . . , ani−1 ), p hat den Inhalt ni−1 und si−1 = (A[ni−1], . . . , A[1]). Fall 1: Opi = s.push(x). – Dann ist si = (x, a1, . . . , ani−1 ). Wenn ni−1 < m ist, wird von der Prozedur s.push(x) das Objekt x an die Stelle A[ni−1 + 1] gesetzt und p auf den Wert ni−1 + 1 = ni erhöht. Daraus folgt die Induktionsbehauptung. Wenn ni−1 = m ist, tritt in der Implementierung ein Fehler ein, und die Behauptung ist trivialerweise erfüllt. FG KTuEA, TU Ilmenau Algorithmen und Datenstrukturen – SS11 – Kapitel 2 22 Fall 2: Opi = s.top(x). – Dann ist si = (x, a1, . . . , ani−1 ). Wenn ni−1 > 0 ist, wird ani−1 ausgegeben, und das ist auch im mathematischen Modell das Resultat für diesen Schritt. Wenn ni−1 = 0, erzeugt die top-Operation im mathematischen Modell einen Fehler, und es ist nichts mehr zu zeigen. Daraus folgt die Induktionsbehauptung für Schritt i. Die anderen Fälle, entsprechend den anderen Operationen, behandelt man analog. Solche Beweise sind etwas mühselig. Sie werden nicht oft aufgeschrieben. Korrektheitsbeweise auf der Basis eines Axiomensystems sind oft eleganter. FG KTuEA, TU Ilmenau Algorithmen und Datenstrukturen – SS11 – Kapitel 2 23 Zeitaufwand der Arrayimplementierung Satz 2.1.3 Bei der Arrayimplementierung hat jede einzelne Operation Kosten O(1). Das sieht man durch Betrachtung der einzelnen Operationen. Bei der Initialisierung empty() (Konstruktoraufruf) darf das Array nur allokiert, nicht initialisiert werden. Man beobachte, dass hierdurch die Korrektheit nicht beeinträchtigt wird. (Sonst: Kosten Θ(m).) FG KTuEA, TU Ilmenau Algorithmen und Datenstrukturen – SS11 – Kapitel 2 24 Arrayimplementierung ohne Platzprobleme? Verdoppelungsstrategie Vorher: s: p: m 1 2 A: a n n=m a 2 a1 Nachher: s: p: m +1 1 2 A: a n FG KTuEA, TU Ilmenau m a 2 a1 x * Algorithmen und Datenstrukturen – SS11 – Kapitel 2 2m * 25 Arrayimplementierung ohne Platzprobleme? Verdoppelungsstrategie In push(x): Bei Überlaufen des Arrays A nicht einen Laufzeitfehler (oder eine exception) generieren, sondern ein neues, doppelt so großes Array AA allokieren. Einträge aus A nach AA kopieren. AA in A umbenennen/Referenz umsetzen. (altes Array freigeben). Kosten: Θ(m), wo m = aktuelle Zahl der Einträge. FG KTuEA, TU Ilmenau Algorithmen und Datenstrukturen – SS11 – Kapitel 2 26 Kostenanalyse bei Verdoppelungsstrategie: Operationenfolge Op0 = empty, Op1, . . . , OpN Arraygröße am Anfang: m0. Jede Operation hat Kosten O(1), außer wenn die Arraygröße von m0 · 2i−1 auf m0 · 2i wächst. Hier entstehen Kosten Θ(m0 · 2i−1). FG KTuEA, TU Ilmenau Algorithmen und Datenstrukturen – SS11 – Kapitel 2 27 k := Anzahl der push-Operationen in Op1, . . . , OpN ⇒ Zahl der Einträge immer ≤ k, also bei Verdopplung m0 · 2i−1 m0 · 2i: m0 · 2i−1 < k. Sei L maximal mit m0 · 2L−1 < k. Gesamtkosten für die Verdopplungen also: X O(m0 · 2i−1) 1≤i≤L 2 = O m0 · (1 + 2 + 2 + · · · + 2 L = O m0 · 2 = O(k). L−1 ) Gesamtkosten: N · Θ(1) + O(k) = Θ(N ). FG KTuEA, TU Ilmenau Algorithmen und Datenstrukturen – SS11 – Kapitel 2 28 Satz 2.1.4 Wenn ein Stack mit Arrays und der Verdoppelungsstrategie implementiert wird, betragen die Gesamtkosten für N Operationen O(N ). Bemerkung: Bei der Verdoppelungsstrategie gilt weiter: Der gesamte Platzbedarf ist O(k), wenn k die maximale je erreichte Stackhöhe ist. Selbst wenn der Speicher nie bereinigt wird (kleinere, jetzt unbenutzte Arrays nicht freigegeben werden), tritt ein Speicherüberlauf erst ein, wenn die Zahl der Stackeinträge zu einem bestimmten Zeitpunkt mehr Speicher beansprucht als 14 des gesamten zur Verfügung stehenden Speichers. (Beweis: Übung.) FG KTuEA, TU Ilmenau Algorithmen und Datenstrukturen – SS11 – Kapitel 2 29 Vergleich Listen-/Arrayimplementierung: Arrayimplementierung • O(1) Kosten pro Operation, amortisiert“ (im Durch” schnitt über alle Operationen), • Sequentieller, indexbasierter (cachefreundlich), Zugriff im Speicher • Höchstens 50% des allokierten Speicherplatzes ungenutzt. Listenimplementierung • O(1) Kosten pro Operation im schlechtesten Fall (jedoch: Allokierungskosten bei push-Operationen), • Zusätzlicher Platz für Zeiger benötigt. FG KTuEA, TU Ilmenau Algorithmen und Datenstrukturen – SS11 – Kapitel 2 30 Datentyp Indizierte Folgen Stacks + wahlfreier Zugriff“. ” Grunddatentyp: Nat – durch die natürlichen Zahlen N = {0, 1, 2, . . .} interpretiert. A: 1 e 2 3 4 h x a 5 6 c f 7 u 8 z 9 10 l q Operationen, skizziert: read(A, i) liefert Eintrag A[i] an Position i, falls definiert. write(A, i, x) ersetzt Eintrag A[i] an Position i durch x, falls definiert. length(A) liefert aktuelle Länge. addLast(A, x) verlängert Folge, schreibt x an letzte Stelle. removeLast(A) verkürzt Folge. FG KTuEA, TU Ilmenau Algorithmen und Datenstrukturen – SS11 – Kapitel 2 31 Indizierte Folgen – Dynamische Arrays 1. Signatur: Sorten: Elements Arrays Nat Operationen: empty : → Arrays addLast: Arrays × Elements → Arrays removeLast: Arrays → Arrays read: Arrays × Nat → Elements write: Arrays × Nat × Elements → Arrays length: Arrays → Nat FG KTuEA, TU Ilmenau Algorithmen und Datenstrukturen – SS11 – Kapitel 2 32 Indizierte Folgen – Dynamische Arrays über D 2. Mathematisches Modell Sorten: Elements: (nichtleere) Menge D (Parameter) Arrays: Seq(D) Nat: FG KTuEA, TU Ilmenau N Algorithmen und Datenstrukturen – SS11 – Kapitel 2 33 Indizierte Folgen – Dynamische Arrays über D 2. Mathematisches Modell (Forts.) Operationen: empty () = () addLast((a1, . . . , an), x) = (a1, . . . , an, x) removeLast((a1, . . . , an)) = (a1, . . . , an−1), falls n ≥ 1 undefiniert, falls n = 0 ai, falls 1 ≤ i ≤ n read((a1, . . . , an), i) = undefiniert, sonst (a1, . . . , ai−1, x, ai+1, . . . , an), falls 1 ≤ i ≤ n write((a1, . . . , an), i, x) = undefiniert, sonst length((a1, . . . , an)) = n FG KTuEA, TU Ilmenau Algorithmen und Datenstrukturen – SS11 – Kapitel 2 34 Indizierte Folgen – Dynamische Arrays über D Implementierung: Arrayimplementierung wie bei Stacks, mit Verdoppelungsstrategie. Kosten: empty, read, write, length: O(1). addLast: k addLast-Operationen kosten Zeit O(k). removeLast: Einfache Version (Pegel dekrementieren): O(1). Verfeinerung: Halbierung, wenn Array (Länge m) zu groß für die Anzahl n der Einträge, z. B. wenn durch eine removeLast-Operation n < 14 · m wird. Nachteil: Diese Operation kostet Θ(n) Zeit. Vorteil: Arraylänge ist stets ≤ 4n. FG KTuEA, TU Ilmenau Algorithmen und Datenstrukturen – SS11 – Kapitel 2 35 Indizierte Folgen – Dynamische Arrays über D Mitteilung Startend mit einem leeren dynamischen Array, kosten k addLast- und removeLast-Operationen insgesamt Zeit O(k). FG KTuEA, TU Ilmenau Algorithmen und Datenstrukturen – SS11 – Kapitel 2 36 2.2 Queues – Warteschlangen – FIFO-Listen∗ ∗ First-In-First-Out Idee (siehe AuP-Vorlesung): vorne hinten 3 1 4 1 5 9 2 6 "head" isempty: "tail" false first: 3 enqueue(5): 3 1 4 1 5 9 2 6 5 dequeue: 1 4 1 5 9 2 6 5 3 FG KTuEA, TU Ilmenau Algorithmen und Datenstrukturen – SS11 – Kapitel 2 37 Spezifikation des Datentyps Queue“ über D ” 1. Signatur: Sorten: Elements Queues Boolean Operationen: empty : → Queues enqueue: Queues × Elements → Queues dequeue: Queues → Queues first: Queues → Elements isempty : Queues → Boolean Beachte: Die Signatur ist identisch zur Signatur von Stacks – bis auf Umbenennungen. Rein syntaktische Vorschriften! Verhalten (noch) ungeklärt! FG KTuEA, TU Ilmenau Algorithmen und Datenstrukturen – SS11 – Kapitel 2 38 Spezifikation des Datentyps Queue“ über D ” 2. Mathematisches Modell Sorten: Elements: (nichtleere) Menge D (Parameter) Queues: Seq(D) Boolean: {true, false} Die leere Folge () stellt die leere Warteschlange dar. FG KTuEA, TU Ilmenau Algorithmen und Datenstrukturen – SS11 – Kapitel 2 39 Spezifikation des Datentyps Queue“ über D ” 2. Mathematisches Modell (Forts.) Operationen: empty () = () enqueue((a1, . . . , an), x) := (a1, . . . , an, x) dequeue((a1, . . . , an)) := (a2, . . . , an), falls n ≥ 1 undefiniert, falls n = 0 a1, falls n ≥ 1 first((a1, . . . , an)) := undefiniert, falls n = 0 false, falls n ≥ 1 isempty ((a1, . . . , an)) := true, falls n = 0 FG KTuEA, TU Ilmenau Algorithmen und Datenstrukturen – SS11 – Kapitel 2 40 Listenimplementierung von Queues Implementierung mittels einfach verketteter Listen: Übung. – Skizze: hinten vorne 3 1 4 1 5 9 2 6 tail: head: 3 1 4 1 5 9 2 6 Variante: Dummy-Element am Listenende. FG KTuEA, TU Ilmenau Algorithmen und Datenstrukturen – SS11 – Kapitel 2 41 Arrayimplementierung von Queues vorne hinten 3 1 4 1 5 9 2 6 "head" "tail" m−2 * * m−1 0 * * 1 2 * * * tail: t 4 * * * * * t * * 3 6 1 2 9 4 5 1 FG KTuEA, TU Ilmenau h 3 * * head: h Algorithmen und Datenstrukturen – SS11 – Kapitel 2 42 Arrayimplementierung von Queues Implementierung mittels Arrays, zirkulär benutzt: In Array A[0..m − 1] sind durch den Inhalt h von head und den Inhalt t von tail zwei Positionen markiert. Die Idee ist, dass die Arrayeinträge A[h],A[h + 1],. . . ,A[t − 1] die Einträge (a1, . . . , an) der Warteschlange bilden, von vorne nach hinten. Dabei werden die Indizes modulo m gerechnet; auf m − 1 folgt also 0. Die Zelle A[t] ist immer leer. Um x einzufügen, wird (solange Platz ist), x an die Stelle A[tail] geschrieben und dann tail um 1 erhöht. FG KTuEA, TU Ilmenau Algorithmen und Datenstrukturen – SS11 – Kapitel 2 43 Arrayimplementierung von Queues (Forts.) Maximale Zahl der Einträge im Array (Kapazität) ist m − 1, da die Zelle mit Index t immer leer sein muss. Ein Überlauf wird ausgelöst, wenn beim Einfügen die Variable tail denselben Wert erhalten würde wie head. Um in einer nichtleeren Warteschlange dequeue auszuführen, wird head um 1 weitergezählt (modulo m). Wenn bei einem dequeue die Warteschlange komplett geleert wird, steht in head und tail derselbe Wert. Wir benutzen die Bedingung head = tail als Leerheitstest. Um die Warteschlange (bei empty ()) zu initialisieren, setzen wir head und tail auf denselben Wert, z.B. 0. FG KTuEA, TU Ilmenau Algorithmen und Datenstrukturen – SS11 – Kapitel 2 44 Implementierung der Operationen auf einer Queue q: q.empty(): (∗ Konstruktor; Option: m als Argument ∗) Erzeuge Array A[0..m − 1] of elements und 2 Variable head, tail: int ; head ← 0; tail ← 0; q.isempty: if head = tail then return true“ else return false“ ; ” ” q.first: if head = tail then Fehler“ (z.B. QEmptyException) ” else return A[head] ; q.dequeue: if head = tail then Fehler“ (z.B. QEmptyException) ” else head ← (head + 1) mod m q.enqueue(x): tt ← (tail + 1) mod m; if tt = head then Fehler“ (z.B. QOverflowException) ” else A[tail] ← x ; tail ← tt ; FG KTuEA, TU Ilmenau Algorithmen und Datenstrukturen – SS11 – Kapitel 2 45 Alternative zu Overflow-Exception: Verdoppelungsstrategie Erzeugen eines neuen Arrays AA[0..2m − 1] (mit Kapazität 2m − 1); Umkopieren der Einträge A[head], A[(head + 1) mod m], . . . , A[(tail − 1) mod m] in AA[0], AA[1], . . . , AA[m − 2]; head ← 0; tail ← m − 1; Umbenennen von AA in A. A[tail] ← x ; tail++ ; FG KTuEA, TU Ilmenau Algorithmen und Datenstrukturen – SS11 – Kapitel 2 46 Satz 2.2.1 Die angegebene Arrayimplementierung einer Queue über D ist korrekt, ohne Verdoppelungsstrategie, solange kein Fehler im Modell und kein Überlauf im Array eintritt; mit Verdoppelungsstrategie, solange kein Fehler im Modell und kein Speicherüberlauf auftritt. Korrektheit“ heißt wie bei Stacks: Das E/A-Verhalten der ” Implementierung ist das gleiche wie beim mathematischen Modell. Beweis: Betrachte eine Folge Op0 = empty, Op1, . . . , OpN von Queueoperationen, wobei empty nur ganz am Anfang vorkommen darf. FG KTuEA, TU Ilmenau Algorithmen und Datenstrukturen – SS11 – Kapitel 2 47 Zeige dann durch Induktion über i = 0, . . . , N unter der Annahme, dass im mathematischen Modell kein Fehler auftritt, folgende Behauptung (IB)i Wenn nach Operationen Op0, Op1, . . . , Opi der Inhalt der Queue qi = (a1, . . . , ani ) ist, dann stehen nach Ausführung der Operationen in der Implementierung die Elemente a1, . . . , ani in den Positionen A[h],A[h + 1],. . . ,A[t − 1], (Indizes modulo m gerechnet), wobei h der Inhalt von head und t der Inhalt von tail ist. Insbesondere ist die Warteschlange leer genau dann wenn h = t ist. FG KTuEA, TU Ilmenau Algorithmen und Datenstrukturen – SS11 – Kapitel 2 48 Satz 2.2.2 Wenn eine Queue mit Arrays und der Verdoppelungsstrategie implementiert wird, sind die Gesamtkosten für N Operationen O(N ). Beweis: Genauso wie bei Stacks. Bemerkung: Bei der Verdoppelungsstrategie gilt weiter: Der gesamte Platzbedarf ist O(k), wenn k die maximale je erreichte Länge der Queue ist. FG KTuEA, TU Ilmenau Algorithmen und Datenstrukturen – SS11 – Kapitel 2 49 Datentyp Doppelschlange – Deque Schlange mit zwei Enden; Einfügen und Entnehmen an beiden Enden möglich. Schema: vorn: 1 hinten 4 1 5 9 Einfache Implementierung: first last: 1 4 1 5 9 Implementierung mit Dummyelementen: last: first: f 1 FG KTuEA, TU Ilmenau 4 1 5 Algorithmen und Datenstrukturen – SS11 – Kapitel 2 9 l 50 1. Signatur: Operationen: empty : . . . pushFront: . . . popFront: . . . first: . . . pushBack: . . . popBack: . . . last: . . . isempty : . . . Details 2. Mathematisches Modell Übung. Implementierung mit Arrays Implementierung mit doppelt verketteten Listen Sorten: Elements Deques Boolean FG KTuEA, TU Ilmenau Algorithmen und Datenstrukturen – SS11 – Kapitel 2 51 2.3 Einfache Datenstrukturen für Mengen Modelliere: Endliche Mengen über D. Intuitive Aufgabe: Speichere eine veränderliche Menge S ⊆ D, S endlich, mit Operationen: Initialisierung: Suche: Hinzufügen: Löschen: FG KTuEA, TU Ilmenau S ← ∅; x ∈ S ?; S ← S ∪ {x}; S ← S − {x}. (∗ (∗ (∗ (∗ anfangs ist S leer ∗) teste, ob x in S ist ∗) füge x zu S hinzu ∗) entferne x aus S ∗) Algorithmen und Datenstrukturen – SS11 – Kapitel 2 52 Datentyp Dynamische Menge 1. Signatur: Sorten: Elements Sets Boolean Operationen: empty : → Sets insert: Sets × Elements → Sets delete: Sets × Elements → Sets member : Sets × Elements → Boolean FG KTuEA, TU Ilmenau Algorithmen und Datenstrukturen – SS11 – Kapitel 2 53 2. Mathematisches Modell: Modellierung einer Menge als endliche Menge von Objekten: Sorten: Elements: (nichtleere) Menge D (Parameter) Sets: P <∞(D) = {S ⊆ D | S endlich} Boolean: {true, false} Operationen: empty () = ∅ insert(S, x) := S ∪ {x} delete(S, x) := S − {x} ( true, falls x ∈ S member (S, x) := false, falls x ∈ /S FG KTuEA, TU Ilmenau Algorithmen und Datenstrukturen – SS11 – Kapitel 2 54 Implementierungsmöglichkeiten (A) Einfach verkettete Liste oder Array mit Wiederholung (B) Einfach verkettete Liste oder Array ohne Wiederholung (C) Einfach verkettete Liste oder Array, aufsteigend sortiert Nur möglich, wenn es auf D eine totale Ordnung < gibt. Später: (D) Suchbäume (E) Hashtabellen FG KTuEA, TU Ilmenau Algorithmen und Datenstrukturen – SS11 – Kapitel 2 55 Array, aufsteigend sortiert: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 2 5 7 10 17 19 26 50 54 59 67 ∗ ∗ ∗ ∗ Pegel“ p: 11. ” Nachteil: insert, delete: Verschieben vieler Elemente nötig, um die Ordnung zu erhalten und dabei das Entstehen von Lücken zu vermeiden. Zeitaufwand: Θ(|S|) Vorteil: member: Kann binäre Suche benutzen. FG KTuEA, TU Ilmenau Algorithmen und Datenstrukturen – SS11 – Kapitel 2 56 Binäre Suche, rekursiv Aufgabe: Suche Objekt x ∈ D in Teilarray A[a..b] Idee: 0. Falls b < a, ist x nicht im Teilarray 1. Falls a = b, ist x in A[a..b], falls x = A[a], sonst nicht 2. Falls a < b, berechne m = b(a+b)/2c (Mitte des Teilarrays) 3 Fälle: x = A[m]: gefunden 1 A: FG KTuEA, TU Ilmenau a 11111 00000 00000 11111 00000 11111 00000 11111 m b 1111111 0000000 0000000 x1111111 0000000 1111111 0000000 1111111 Algorithmen und Datenstrukturen – SS11 – Kapitel 2 n 57 Binäre Suche, rekursiv x < A[m]: Wende binäre Suche auf A[a..m − 1] an 1 a m b 0000000 1111111 n 0000000 z1111111 >x 0000000 1111111 0000000 1111111 A: x<z x > A[m]: Wende binäre Suche auf A[m + 1..b] an 1 A: a 11111 00000 00000 11111 <x 00000 11111 00000 11111 m b n z z<x Suche nach x in A[1..p]: Binäre Suche in A[1..p] FG KTuEA, TU Ilmenau Algorithmen und Datenstrukturen – SS11 – Kapitel 2 58 Gegeben: Array A[1..M ], x: elements; Prozedur RecBinSearch(a, b) Eingabe: a, b: int; (∗ 1 ≤ a ≤ M + 1, 0 ≤ b ≤ M ∗) (∗ Prozedur sucht global bekanntes x unter den A[i], a ≤ i ≤ b ∗) (1) if b < a then return false; (2) if a = b then return (x = A[a]); (3) m ← (a + b) div 2; (4) if x = A[m] then return true; (5) if x < A[m] then return RecBinSearch(a,m−1); (6) if x > A[m] then return RecBinSearch(m+1,b). Algorithmus Binäre Suche (rekursiv) Eingabe: Array A[1..M ]; x: elements; n, 1 ≤ n ≤ M , mit A[1] < · · · < A[n]; (1) return RecBinSearch(1, n). FG KTuEA, TU Ilmenau Algorithmen und Datenstrukturen – SS11 – Kapitel 2 59 Korrektheit von Prozedur RecBinSearch: Proposition 2.3.1 Das Teilarray A[a..b] von A[1..M ] sei strikt aufsteigend sortiert, und es gelte 1 ≤ a ≤ M + 1, 0 ≤ b ≤ M . Dann gilt: Der Aufruf RecBinSearch(a, b) liefert das Resultat true, falls x in A[a..b] vorkommt, false, falls nicht. Beweis: (∗) Die Aussage stimmt, falls a > b (Zeile (1)) Beh.: Wenn b − a + 1 = i ≥ 1, dann liefert der Aufruf RecBinSearch(a, b) mit 1 ≤ a, b ≤ M das korrekte Resultat. Beweis der Beh. durch (verallgemeinerte) Induktion über i. I.A.: Wenn i = 1, ist a = b, und die Ausgabe ist korrekt (Zeile (2)). FG KTuEA, TU Ilmenau Algorithmen und Datenstrukturen – SS11 – Kapitel 2 60 I.V.: Die Beh. gilt für alle Aufrufe RecBinSearch(c, d) mit 1 ≤ d − c + 1 < i. Ind.-Schritt: Sei i > 1. – Dann ist a < b. Es wird m = b(a + b)/2c berechnet. Beobachte: a ≤ m < b. Fall 1: x = A[m]. – Ausgabe true korrekt (Zeile (4)) Fall 2: x < A[m]. – Beobachte: Wenn x in A[a..b] vorkommt, dann nicht in A[m..b], wegen der Sortierung. Es genügt also, x im restlichen Teil A[a..m − 1] zu suchen. Wir überlegen: Der rekursive Aufruf RecBinSearch(a, m − 1) in Zeile (5) liefert das korrekte Resultat. (Beachte: m − 1 ≥ 0.) Wieso? Falls m − 1 < a, benutze (∗). FG KTuEA, TU Ilmenau Algorithmen und Datenstrukturen – SS11 – Kapitel 2 61 Falls m − 1 ≥ a, ist (m − 1) − a + 1 < b − a + 1 = i, also gilt nach der I.V. für (a, m − 1): Aufruf RecBinSearch(a, m − 1) liefert true genau dann wenn x in A[a..m − 1] vorkommt. Fall 3: x > A[m]. – Analog zu Fall 2. Satz 2.3.1 Der Algorithmus Binäre Suche auf einem strikt aufsteigend geordneten Teilarray A[1..n] liefert das korrekte Ergebnis. Beweis: Direkt aus der Induktionsbehauptung für i = n. FG KTuEA, TU Ilmenau Algorithmen und Datenstrukturen – SS11 – Kapitel 2 62 Laufzeit von Prozedur RecBinSearch: TRBS (i) seien die worst-case Kosten für einen Aufruf RecBinSearch(a, b) mit b − a + 1 = i (maximiert über alle möglichen Arrays A[a..b] und Indexpaare (a, b)) Jeder Aufruf von RecBinSearch verursacht Kosten ≤ C für eine Konstante C, wenn man die Kosten der hiervon ausgelösten rekursiven Aufrufe nicht zählt. Gesucht also, für alle i: r(i) = maximale Anzahl der Aufrufe, inklusive des ersten Aufrufs, wenn b − a + 1 = i. Klar nach Zeilen (1), (2): r(0) = r(1) = 1. FG KTuEA, TU Ilmenau Algorithmen und Datenstrukturen – SS11 – Kapitel 2 63 Für i ≥ 2: Die rekursiven Aufrufe beziehen sich auf Teilarrays der Länge b(i − 1)/2c (links) oder d(i − 1)/2e (rechts). ⇒ Rekurrenzungleichung: r(i) ≤ 1 + max{r(b(i − 1)/2c), r(d(i − 1)/2e)}. Behauptung: r(i) ≤ 1 + log i, für i ≥ 1. Beweis: Verallgemeinerte Induktion. I.A.: Korrekt für i = 1, weil log 1 = 0. I.S.: Nun sei i ≥ 2. Nach Rekurrenz: r(i) ≤ 1 + max{r(b(i − 1)/2c), r(d(i − 1)/2e)}. Falls i = 2, heißt das r(2) ≤ 1 + max{r(0), r(1)} = 2 = 1 + log 2. FG KTuEA, TU Ilmenau Algorithmen und Datenstrukturen – SS11 – Kapitel 2 64 Falls i ≥ 3, ist nach der Rekurrenz und der I.V. r(i) ≤ 1 + max{1 + log(b(i − 1)/2c), 1 + log(d(i − 1)/2e)} ≤ 2 + log(i/2) = 1 + log i ; das ist die Induktionsbehauptung. Satz 1.3.2 Der Algorithmus Binäre Suche benötigt auf Eingabe A[1..n] höchstens 1 + blog nc rekursive Aufrufe und Kosten (Laufzeit) O(log n). Beispiel: Arraygröße 10 000 000 erfordert nicht mehr als 1 + blog2(107)c = 24 rekursive Aufrufe für eine Suche. FG KTuEA, TU Ilmenau Algorithmen und Datenstrukturen – SS11 – Kapitel 2 65 Binäre Suche in schwach geordneten Arrays Variante der Aufgabenstellung (häufig in Anwendungen): Das (Teil-)Array A[1..n] ist schwach aufsteigend“ sortiert, ” d.h. es gilt A[1] ≤ · · · ≤ A[n] . Die Ausgabe zum Input x ist informativer als nur die true/false-Entscheidung, wie bisher betrachtet. Sie besteht aus zwei Teilen ww und i(x). Der Wahrheitswert ww ∈ {true, false} hat dieselbe Bedeutung wie bisher. FG KTuEA, TU Ilmenau Algorithmen und Datenstrukturen – SS11 – Kapitel 2 66 Binäre Suche in schwach geordneten Arrays 8 9 10 11= n 1 2 3 4 5 6 7 A: 4 4 7 7 7 9 9 10 12 15 15 * * * i (7) = 3 erstes Vorkommen i (8) = 6 Position des ersten größeren i (20) = 12 Eintrag größer als alle Arrayeinträge Allgemein: i(x ) 1 <x FG KTuEA, TU Ilmenau n x Algorithmen und Datenstrukturen – SS11 – Kapitel 2 * * * 67 Binäre Suche in schwach geordneten Arrays Zudem definieren wir: i(x) = min({i | 1 ≤ i ≤ n, x ≤ A[i]} ∪ {n + 1}). Wenn x im Array vorkommt, ist dies die Position des ersten (am weitesten links stehenden) Vorkommens von x. Wenn x nicht vorkommt, ist es die Position des ersten Eintrags, der größer als x ist. Wenn kein Eintrag im Array größer als x ist, ist i(x) = n + 1. FG KTuEA, TU Ilmenau Algorithmen und Datenstrukturen – SS11 – Kapitel 2 68 Iterative binäre Suche Bei der Algorithmennotation ändern wir ebenfalls den Ansatz: In einer einfachen Schleife (ohne Rekursion) wird die Position i(x) bestimmt. Im Fall der binären Suche ist der Schritt von rekursiver zu iterativer Implementierung leicht, weil es sich um eine tail ” recursion“ handelt: das Resultat des rekursiven Aufrufs wird einfach nach außen durchgereicht“. ” Bei der Suche werden nur ≤-Vergleiche benutzt (keine Gleichheitstests). Anhand von i(x) wird dann in einem Vergleich entschieden, ob x in A[1..n] vorkommt. FG KTuEA, TU Ilmenau Algorithmen und Datenstrukturen – SS11 – Kapitel 2 69 Algorithmus Binäre Suche (Iterativ) Eingabe: Array A[1..M ], x: elements; n, 1 ≤ n ≤ M , mit A[1] ≤ · · · ≤ A[n]. (1) a ← 1; b ← n; (2) while a < b do (3) m ← (a+b) div 2 ; (4) if x ≤ A[m] then b ← m else a ← m + 1; (5) if x > A[a] then return (false, a + 1); (6) if x = A[a] then return (true, a) (7) else return (false, a). FG KTuEA, TU Ilmenau Algorithmen und Datenstrukturen – SS11 – Kapitel 2 70 Satz 1.3.3 Der Algorithmus Binäre Suche (Iterativ) auf einem schwach aufsteigend geordneten Array A[1..n] liefert das korrekte Ergebnis. Die Anzahl der Durchläufe durch den Rumpf der Schleife ist höchstens dlog ne. Die Laufzeit ist Θ(log n) im schlechtesten und im besten Fall. Vorsicht! Die Korrektheit ist alles andere als offensichtlich. Sorgfältiges Argumentieren ist nötig. Beweis: Übung. FG KTuEA, TU Ilmenau Algorithmen und Datenstrukturen – SS11 – Kapitel 2 71 Mehrere Mengen: Datentyp DynSets Intuitive Aufgabe: Speichere mehrere Mengen S1, . . . , Sr ⊆ D, so dass für jede die Operationen des Datentyps Dynamische Menge ausführbar sind und zudem Vereinigung, Durchschnitt, Differenz, Symmetrische Differenz, Leerheitstest Signatur: Wie Dynamische Menge, und zusätzlich: union : Sets × Sets → Sets intersection : Sets × Sets → Sets diff : Sets × Sets → Sets symdiff : Sets × Sets → Sets isempty : Sets → Boolean FG KTuEA, TU Ilmenau Algorithmen und Datenstrukturen – SS11 – Kapitel 2 72 Mathematisches Modell: Wie Dynamische Menge, und zusätzlich: union(S1, S2) = S1 ∪ S2 intersection(S1, S2) = S1 ∩ S2 diff(S1, S2) = S1 − S2 symdiff(S1, S2) = (S1 ∪ S2) − (S1 ∩ S2) ( true, falls S = ∅ isempty (S) = false, sonst. FG KTuEA, TU Ilmenau Algorithmen und Datenstrukturen – SS11 – Kapitel 2 73 Weitere interessante Kombination: Operationen erhält man durch Gleichheitstest: Ist S1 = S2? equal(S1, S2) = isempty(symdiff(S1, S2)) Teilmengentest: Ist S1 ⊆ S2? subset(S1, S2) = isempty(diff(S1, S2)) Disjunktheitstest: Ist S1 ∩ S2 = ∅? disjoint(S1, S2) = isempty(intersection(S1, S2)) FG KTuEA, TU Ilmenau Algorithmen und Datenstrukturen – SS11 – Kapitel 2 74 Implementierung des Datentyps DynSets Einfach: Ungeordnete Strukturen (Array/Liste) mit oder ohne Wiederholung. Implementierung von isempty : teste, ob Liste leer bzw. Pegelstand gleich 0. union(S1, S2): Mit Wiederholung: Darstellung von Si hat Länge hi, i = 1, 2. Kosten: Bei Listen O(1) (halte Zeiger auf Listenende!) bei Arrays O(h1 + h2) (muss kopieren). Für intersection(S1, S2), diff (S1, S2), symdiff (S1, S2): Für jedes Element der Darstellung von S1 durchsuche Darstellung von S2. Kosten: Θ(h1 · h2). FG KTuEA, TU Ilmenau Algorithmen und Datenstrukturen – SS11 – Kapitel 2 75 Etwas schlauer: Aufsteigend sortierte Arrays/Listen. Implementierungen der Operationen union(S1, S2), intersection(S1, S2), diff (S1, S2), symdiff (S1, S2): Paralleler Durchlauf durch zwei Arrays/Listen, analog zum Mischen (Merge) bei Mergesort. FG KTuEA, TU Ilmenau Algorithmen und Datenstrukturen – SS11 – Kapitel 2 76 Hier: Mit uneigentlichem Wächterelement“ (engl.: sentinel“) ” ” Eintrag: ∞, größer als alle Elemente von U . – Eingabe: 3 7 20 25 4 11 20 oo oo Ausgeführte Vergleiche und Ergebnis bei union: 3 4 7 4 7 11 20 11 20 20 25 oo oo oo 3 4 7 11 20 25 oo Kosten: Θ(n1 + n2), wo |S1| = n1, |S2| = n2. Für die anderen Operationen: analog. FG KTuEA, TU Ilmenau Algorithmen und Datenstrukturen – SS11 – Kapitel 2 77 Für kleine“ Mengen: Bitvektor-Darstellung ” Grundmenge D gegeben als konstantes Tupel (x1, . . . , xN ) Identifiziere xi mit i, d.h. D = {1, . . . , N } Beispiel: In Pascal: type Farbe = (rot, gruen, blau, gelb, lila, orange); Palette = set of Farbe; Hier: N = 6. rot = b 1, gruen = b 2, blau = b 3, gelb = b 4, lila = b 5, orange = b 6. FG KTuEA, TU Ilmenau Algorithmen und Datenstrukturen – SS11 – Kapitel 2 78 Repräsentation: Durch Array A[1..N ] of Boolean wird S = {i | A[i] = 1} dargestellt. Wie immer: 0 für false, 1 für true. Beispiel: Darstellung der Menge {2, 3, 5} = b {gruen, blau, lila}: 0 1 1 0 1 0 Platzbedarf: N Bits, d. h. dN/8e Bytes oder dN/32e Speicherworte à 32 Bits. FG KTuEA, TU Ilmenau Algorithmen und Datenstrukturen – SS11 – Kapitel 2 79 empty : Lege Array A[1..N ] an; for i from 1 to N do A[i] ← 0 ; Kosten: Θ(N ) insert(S, i): A[i] ← 1 ; Kosten: O(1) delete(S, i): A[i] ← 0 ; Kosten: O(1) member (S, i): return A[i]; Kosten: O(1) DynSets-Operationen: union(S1, S2), intersection(S1, S2), diff (S1, S2), symdiff (S1, S2): Jeweils paralleler Durchlauf durch zwei Arrays. Kosten: Θ(N ) isempty (S): Durchlauf durch ein Array. FG KTuEA, TU Ilmenau Algorithmen und Datenstrukturen – SS11 – Kapitel 2 Kosten: Θ(N ) 80 Zusatz 1: Wenn man zur Bitvektor-Darstellung von |S| eine int-Variable hinzufügt, die |S| enthält, dauert der Leerheitstest nur O(1), die anderen Operationen nur um einen konstanten Faktor länger. Übung: Wie ist die Implementierung von empty, insert, delete, union, symdiff zu modifizieren, wenn ein solcher Zähler mitgeführt wird? Zusatz 2: Im Fall der Bitvektor-Darstellung lässt sich auch die Komplement-Operation compl : Sets → Sets mit der Semantik (im mathematischen Modell) compl(S) = U − S leicht implementieren. Kosten: Θ(N ) FG KTuEA, TU Ilmenau Algorithmen und Datenstrukturen – SS11 – Kapitel 2 81 Kosten für DynSets-Operationen, einfache Implementierung: BV AmW LmW {A,L}oW A-sort L-sort empty insert delete member isempty Θ(N ) O(1) O(1) O(1) O(1) O(1) O(1) O(h) O(h) O(1) O(1) O(1) O(h) O(h) O(1) O(1) O(n) O(n) O(n) O(1) O(1) O(n) O(n) O(log n) O(1) O(1) O(n) O(n) O(n) O(1) union i’section diff symdiff compl Θ(N ) Θ(N ) Θ(N ) Θ(N ) Θ(N ) O(h1 +h2) O(h1h2) O(h1h2) O(h1h2) − O(1) O(h1h2) O(h1h2) O(h1h2) − O(n1n2) O(n1n2) O(n1n2) O(n1n2) − O(n1 +n2) O(n1 +n2) O(n1 +n2) O(n1 +n2) − O(n1 +n2) O(n1 +n2) O(n1 +n2) O(n1 +n2) − BV: Bitvektor; A: Array; o: ohne; m: mit; W: Wiederholung; A/L-sort: sortierte(s) Array/Liste N : Länge des Bitvektors; n, n1, n2: Größe von S, S1, S2 h, h1, h2: Länge der Listen-/Arraydarstellung von S, S1, S2 FG KTuEA, TU Ilmenau Algorithmen und Datenstrukturen – SS11 – Kapitel 2 82 2.4. Der Datentyp Wörterbuch Synonym: Dictionary“, Dynamische Abbildung“ ” ” Gegeben ist Schlüsselmenge U und eine Wertemenge R (engl. range“) ” Manchen Schlüsseln x ∈ U soll ein Wert f (x) ∈ R zugeordnet sein. Menge der möglichen Datensätze: D = U × R. Man möchte: • • • • empty : ein leeres Wörterbuch erzeugen lookup: den einem Schlüssel zugeordneten Wert finden insert: neue Paare (Schlüssel, Wert) einführen delete: anhand eines Schlüssels ein Paar löschen FG KTuEA, TU Ilmenau Algorithmen und Datenstrukturen – SS11 – Kapitel 2 83 Beispiel: U R Max 2.1. f S 31.8. Luise 30.5. Frank Klara Peter Mario FG KTuEA, TU Ilmenau Algorithmen und Datenstrukturen – SS11 – Kapitel 2 5.2. 26.9. 17.12. 84 Beispiel mit U = Menge der endlichen Folgen von Buchstaben A,B,. . .,Z,a,b,. . .,z und R = {1, . . . , 31} × {1, . . . , 12}: Befehl empty () insert(Frank, 30.5.) insert(Klara, 17.12.) insert(Peter, 2.1.) lookup(Klara) lookup(Luise) FG KTuEA, TU Ilmenau Wörterbuch ∅ {(Frank, 30.5.)} {(Frank, 30.5.), (Klara, 17.12.)} {(Frank, 30.5.), (Klara, 17.12.), (Peter, 2.1.)} ” ” ” ” Algorithmen und Datenstrukturen – SS11 – Kapitel 2 Ausgabe ok“ ” ok“ ” ok“ ” 17.12.“ ” ↑“ ” 85 insert(Luise, 31.8.) insert(Klara, 5.2.) lookup(Klara) delete(Peter) lookup(Peter) delete(Mario) FG KTuEA, TU Ilmenau {(Frank, 30.5.), (Klara, 17.12.), (Peter, 2.1.), (Luise, 31.8.)} {(Frank, 30.5.), (Klara, 5.2.), (Peter, 2.1.), (Luise, 31.8.)} ” ” {(Frank, 30.5.), (Klara, 5.2.), (Luise, 31.8.)} ” ” ” ” Algorithmen und Datenstrukturen – SS11 – Kapitel 2 ok“ ” !!“ ” 5.2.“ ” o.k.“ ” ↑“ ” !!“ ” 86 Datentyp Wörterbuch 1. Signatur: Sorten: Keys Range Maps Operationen: empty : → Maps insert: Maps × Keys × Range → Maps delete: Maps × Keys → Maps lookup: Maps × Keys → Range FG KTuEA, TU Ilmenau Algorithmen und Datenstrukturen – SS11 – Kapitel 2 87 2. Mathematisches Modell: Sorten: Keys: Menge U (Menge aller Schlüssel) Range: Menge R (Menge aller Werte) Maps: {f | f : S → R für ein S ⊆ U , |S| < ∞} Erinnerung: f ist Abbildung/Funktion von S nach R, wenn f ⊆ S × R und ∀x ∈ S ∃!r ∈ R : (x, r) ∈ f . ∃!“: es gibt genau ein“ ” ” Für S schreiben wir D(f ) (Definitionsbereich) Falls x ∈ D(f ), ist f (x) das eindeutig bestimmte r ∈ R mit (x, r) ∈ f . FG KTuEA, TU Ilmenau Algorithmen und Datenstrukturen – SS11 – Kapitel 2 88 Operationen: empty () = ∅. ( f (x), falls x ∈ D(f ) lookup(f, x) := undefiniert“, sonst. ( ” f − {(x, f (x))}, falls x ∈ D(f ) delete(f, x) := f, sonst. ( (f − {(x, f (x))}) ∪ {(x, r)}, falls x ∈ D(f ) insert(f, x, r) := f ∪ {(x, r)}, sonst. FG KTuEA, TU Ilmenau Algorithmen und Datenstrukturen – SS11 – Kapitel 2 89 Implementierungsmöglichkeiten • Listen/Arrays, Einträge aus U × R, mit Wiederholung von Schlüsseln (das erste Vorkommen eines Schlüssels zählt) • Listen/Arrays, Einträge aus U × R, ohne Wiederholung von Schlüsseln, unsortiert oder sortiert • Wenn U = {1, . . . , N }: Array f[1..N ] mit Werten in R ∪ {⊥}, ⊥ = b undefiniert. • Suchbäume und Hashtabellen (später) Zeiten für Operationen wie bei Implementierungen des Datentyps DynSets. FG KTuEA, TU Ilmenau Algorithmen und Datenstrukturen – SS11 – Kapitel 2 90