Departement Mathematik und Informatik Algorithmen und Datenstrukturen, FS17 Prof Dr Christian Tschudin 15. März 2017 – Amortisierte Analyse – Binäre Bäume – Baum-Traversierung Grace Hopper Algorithmen und Datenstrukturen, FS17 15. März 2017 — 2 / 27 Uebersicht 2017-03-15 I Ergänzung zur Vorlesung vom 2. März: – n2 , n3 etc sind unabhäng. Komplexitätsklassen – Felddarstellung dynamischer Listen I Drei-Summen-Algorithmus mit O(n2 log n) I Amortisierte Analyse – redimensionierb. Felder und “harmloser” Aufwand I Baum-ADT: – Definition, binärer Baum, Syntax-Baum – Grace Hopper – Baum-Traversierung Algorithmen und Datenstrukturen, FS17 15. März 2017 — 3 / 27 Wiederholung I Was bedeutet Θ(. . .) ? I Für was steht der Kleene-Stern ? I Was ist ein “regulärer Ausdruck”? Algorithmen und Datenstrukturen, FS17 15. März 2017 — 4 / 27 Wichtige Komplexitätsklassen (Korr.) Familie konstant logarithmisch linear linearithmetisch polynomial exponentiell Repräsentant Beispiel-Problem 1 log n n n log n n3 n4 ... 2n Addition Binary Search Maximum-Suche Merge-Sort naives Drei-Summe Knapsack Algorithmen und Datenstrukturen, FS17 15. März 2017 — 5 / 27 Verkette Listen mittels Felder (Wiederh.) I Interne, verkette Liste für die freien Slots – innerhalb eines schon vorhandenen Feldes I frei zeigt auf ersten freien Slot – im Nachfolger-Feld wo es weitergeht – Wert -1 um Ende der freien Liste zu markieren Algorithmen und Datenstrukturen, FS17 15. März 2017 — 6 / 27 Das Drei-Summen-Problem (Recap) Gegeben: Liste von Integer-Werten, ohne Wiederholungen Gesucht: finde/zähle alle drei Werte deren Summe = 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 public class ThreeSum // (aus Sedgewick and Wayne) { public static int count(int[] a) { // Count triples that sum to 0. int N = a.length; int cnt = 0; for (int i = 0; i < N; i++) for (int j = i+1; j < N; j++) for (int k = j+1; k < N; k++) if (a[i] + a[j] + a[k] == 0) cnt++; return cnt; } public static void main(String[] args) { int[] a = In.readInts(args[0]); StdOut.println(count(a)); } } Algorithmen und Datenstrukturen, FS17 15. März 2017 — 7 / 27 Verschnellerung Zwei-Summen-Algor. Idee für verschnellerten Zwei-Summen-Algorithmus: – gegeben sei der erste Summand x (fest): – existiert −x in der Liste? – Falls Liste sortiert ist: Antwort in O(log n) – Sortierungskosten (Vorwegnahme): O(n log n) – Total: O(n log n + n(log n)) = O(n log n) 1 2 3 4 5 6 7 8 9 10 11 public static int count2(int[] a) { // Count pairs that sum to 0. Arrays.sort(a); int N = a.length; int cnt = 0; for (int i = 0; i < N; i++) int x = a[i]; if (BinarySearch.rank(-x, a) > i) cnt++; return cnt; } Algorithmen und Datenstrukturen, FS17 15. März 2017 — 8 / 27 Verschnellerung Drei-Summen-Algor. Idee für verschnellerten Drei-Summen-Algorithmus: – seien die beiden erste Summanden x und y (fest): – existiert −(x + y) in der Liste? – Laufzeit: O(n log n + n2 (log n)) = O(n2 log n) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 public static int count3(int[] a) { // Count triples that sum to 0. Arrays.sort(a); int N = a.length; int cnt = 0; for (int i = 0; i < N; i++) int x = a[i]; for (int j = i+1; j < N; j++) { int y = a[j]; if (BinarySearch.rank(-(x+y), a) > j) cnt++; } return cnt; Warum ist Vergleich } >j richtig? Algorithmen und Datenstrukturen, FS17 15. März 2017 — 9 / 27 “Amortisierte Analyse” Vorgehen (in den nächsten Slides) 1. Recap Kellerstapel-Implementierung mittels Feld 2. Kellerstapel mit redimensionierbarem Feld 3. Kosten der Feld-Redimensionierung 4. Amortisierte Analyse Algorithmen und Datenstrukturen, FS17 15. März 2017 — 10 / 27 Kellerstapel mittels Feld (Repetition) I I1 2 3 4 5 6 7 8 9 10 ADT-Kellerstapel mittels Feld implementieren: S: Speicher (ein Feld), N: maximale Stapel-Länge t: Index-Wert des “obersten” Elements, startet mit 0 push(e): pop(): if size() == N: KELLER_VOLL else: t = t + 1 S[t] = e if isEmpty(): KELLER_LEER else: e = S[t] t = t - 1 // <------ Feste Allokation von N ist ein Problem. Algorithmen und Datenstrukturen, FS17 15. März 2017 — 11 / 27 Feld umdimensionieren (1/2) Feld vergrössern falls kein Platz mehr vorhanden ist: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public void push(String item) { // Add item to top of stack. t++; if (t == S.length) resize(2 * S.length); S[t] = item; } // <-----// <------ private void resize(int newMax) { // Move stack of size t < newMax to a new array of size newMax. Item[] temp = (Item[]) new Object[newMax]; for (int i = 0; i <= t; i++) temp[i] = S[i]; S = temp; } Algorithmen und Datenstrukturen, FS17 15. März 2017 — 12 / 27 Feld umdimensionieren (2/2) Feld verkleinern falls zuviel freier Platz vorhanden ist: 1 2 3 4 5 6 7 8 public String pop() { // Remove item from top of stack. String item = S[t]; S[t--] = null; // avoid loitering if (t > 0 && t == S.length/4) resize(S.length/2); return item; } // <-----// <------ Strategie: – nur schrumpfen wenn weniger als 1/4 gefüllt ist – und nur um Hälfte schrumpfen (Reserve belassen) Hysteresis verhindert “Oszillation” Algorithmen und Datenstrukturen, FS17 15. März 2017 — 13 / 27 Diskussion “Feld umdimensionieren” Wie wirkt sich das Vergrössern und Schrumpfen auf die Komplexitätsklasse aus? I Bei vorgegebener Feldgrösse galt bisher: push() und pull() sind O(1) I Bei variabler Feldgrösse gilt neu: – push(): schlimmstenfalls N Elemente umkopieren – pull(): schlimmstenfalls N/4 Elemente umkopieren → O(n) I Frage, wie oft dies vorkommt. Selten? Dann harmlos. Amortisierte Analyse: Vergleiche durchschnittlichen Aufwand pro Methodenaufruf über Gesamtlaufzeit Gesamtkosten (inkl Extraaufwand) Anzahl Methodenaufrufe Algorithmen und Datenstrukturen, FS17 15. März 2017 — 14 / 27 Amortisierte Analyse (1/2, Beispiel) Leseanleitung für M Methodenaufrufe, M=65, dann M=128: Sei Feld schon mit 64 Elementen gefüllt. Das 65ste push() löst eine Verdopplung aus, nötig werden je 1 Lese- und Schreibzugriff um jedes der 64 Elemente umzukopieren → 128 Speicherzugriffe. Alle 63 weiteren push() benötigen keinen Extraaufwand. Verteile die 128 (+64, +32, +16 . . . ) Extrakosten auf die 128 push()-Aufruf: → Durchschnitt ist 5*M. Algorithmen und Datenstrukturen, FS17 15. März 2017 — 15 / 27 Amortisierte Analyse (2/2, Diskussion) Ohne Feld-Redimensionierung hängt Laufzeit nicht von N ab: 1 Speicherzugriff pro push() I I I I Mit der Feld-Redimensionierungstechnik: – einzelnes push() kann O(n) Aufwand auslösen Amortisierte Analyse, bei M konsekutiven push(): – durchschnittlicher Aufwand ist 5 M pro push() – d.h. konstante Laufzeitkomplexität, O(1) M.a.W: Laufzeit hängt weiterhin nicht von N ab, trotz Feld-Redimensionierung-Technik. (Aber Aufwand pro push() nun 5 mal höher.) Algorithmen und Datenstrukturen, FS17 Binärer Baum – Rekursive Definition Ein binärer Baum ist – entweder ein Blatt, oder – ein interner Knoten mit 2 Kindern, – die selber binäre Bäume sind. 15. März 2017 — 16 / 27 Algorithmen und Datenstrukturen, FS17 15. März 2017 — 17 / 27 Baum – Begriffe Knote Kante Blatt Wurzel Eltern Kind Geschwister Knotentiefe node edge leaf root parent child sibling depth Knotenhöhe height Baumhöhe Ebene height level Pfadlänge zur Wurzel längster Pfad zu einem Blatt = Wurzelhöhe = Tiefe, auch = Tiefe + 1 Algorithmen und Datenstrukturen, FS17 15. März 2017 — 18 / 27 Baum: Spezialfall eines → Graphen I I I I I “Baum” wurde von 1857 von A. Cayley eingeführt. Baum = ungerichteter Graph wobei beliebige zwei Knoten durch genau 1 Pfad verbunden sind. Jeder azyklische Graph ist ein Baum. Jeder der N Knoten eines Baumes kann als Wurzel dienen (ohne Wurzel: “freier Baum”) Definition “k-ary tree” für Bäume mit Wurzel: Jeder innere Knoten hat höchstens k Kinder (k=2: binärer Baum, k=3: ternärer Baum) Algorithmen und Datenstrukturen, FS17 15. März 2017 — 19 / 27 Binärer Baum – Eigenschaften I I I Anzahl Blätter = Anzahl interne Knoten + 1 Anzahl Knoten auf Ebene i ist ≤ 2i (Wurzel ist auf Ebene 0) Anzahl der Blätter ist ≤ 2h wobei h die Höhe des Baums ist. Algorithmen und Datenstrukturen, FS17 15. März 2017 — 20 / 27 Binärer Baum – Drei Typen I I I Voller binärer Baum = jeder Knoten hat 0 oder 2 Kinder = normale Definition eines binären Baumes Kompletter binärer Baum = alle Ebenen sind vollständig gefüllt ausser evtl. die letzte Ebene wobei “Blätter nur rechts fehlen” Perfekter binärer Baum = = alle internen Knoten haben genau 2 Kinder und alle Blätter sind auf der gleichen Ebene Algorithmen und Datenstrukturen, FS17 15. März 2017 — 21 / 27 Beispiel Bin. Baum – Arithm. Ausdrücke I I Arithmetischer Ausdruck zum obigen Baum: (1 - 3) × 5 + (2 + 3) × (4 - 6) Formel = “Oberflächenstruktur”, Repräsentation Baum = “Tiefenstruktur” Algorithmen und Datenstrukturen, FS17 15. März 2017 — 22 / 27 Grace Hopper 1906–1992 (1/2) I I I Programmierung von “Mark I” (1944) bei der US-Marine danach Harvard, Eckert-Mauchly Computer Corporation, Sperry Rand Corporation (Univac) “Debugging” wird ihr zugeschrieben. Protokollierte 1947 eine Motte, die in ein Relay von “Mark II” geriet Algorithmen und Datenstrukturen, FS17 15. März 2017 — 23 / 27 Grace Hopper (2/2) Hat wichtige Beiträge für die Computer Science geliefert I I I Erkannte Bezug von arithm Ausdrücken und Baum-Struktur Konzept und erste Implementierung von Compilern – automatische Code-Generierung → A-2 (1954) – in einer Zeit, wo Computer nur “rechnen” konnten Heutiger Jargon: eher Linker als Compiler Plädierte für Programme in menschen-naher Sprache – in einer Zeit, wo es nur Assembler-Code gab – Add One To Total statt Total += 1 (→ COBOL) – wichtiger Einfluss auf COBOL, auch Beiträge – zu FORTRAN (FORmula TRANslator) Algorithmen und Datenstrukturen, FS17 15. März 2017 — 24 / 27 Baum-Traversierung (1/4) I I Breitenansatz, breadth-first search, BSF eine Ebene nach der anderen, von links nach rechts Tiefenansatz, depth-first search, DFS zuerst in die Tiefe, dann links nach rechts Algorithmen und Datenstrukturen, FS17 15. März 2017 — 25 / 27 Baum-Traversierung (2/4) Welches ist der richtige Ansatz, um arithmetische Ausdrücke auszuwerten? I I I “Wert an der Wurzel” kann erst berechnet werden, wenn die Werte der Unterbäume bekannt sind Auswertung muss von unten beginnen Wert ausrechnen, nachdem beide Unterbäume besucht wurden → DFS mit sog. “post-order”-Auswertung Algorithmen und Datenstrukturen, FS17 15. März 2017 — 26 / 27 Traversierung (3/4): Drei DFS-Strategien 1. Präorder-Strategie Knoten vor weiterem DFS bearbeiten 2. Inorder-Strategie Knoten zwischen DFS von Unterbäumen bearbeiten 3. Postorder-Strategie Knoten nach dem DFS aller Unterbäume bearbeiten 1 2 3 4 def preorder(node n): process(n) for child in n: preorder(child) Hierarch. Dokument von A bis Z lesen 1 2 3 4 5 def inorder(node n): inorder(n[0]) for child in n[1:]: process(n) inorder(child) Arithm. Ausdrücke “infix” ausdrucken 1 2 3 4 def postorder(node n): for child in n: postorder(child) process(n) Arithm. Ausdrücke auswerten Algorithmen und Datenstrukturen, FS17 15. März 2017 — 27 / 27 Traversierung (4/4): Prefix, Infix, Postfix DFS-Strategie und Ausgabe der Bauminformation führt zu verschiedenen Repräsentationen des Syntax-Baums. Beispiel für arithmetische Ausdrücke: I Prefix: → LISP (+ (+ a (* b c)) (* (+ (* d e) f) g)) I Infix: → TI-Taschenrechner a + b * c + (d * e + f) * g I Postfix: → HP-Taschenrechner a b c * + d e * f + g * + = RPN (reverse polnish notation), braucht keine Klammern