Datenstrukturen und Algorithmen Einführung Komplexität von Algorithmen (Folie 34, Seite 18 im Skript) Java Wir verwenden oft Java für Datenstrukturen und Algorithmen. Die Vorlesung ist aber von der Programmiersprache unabhängig. Lernziel sind die einzelnen Algorithmen und Datenstrukturen, nicht ihre Umsetzung in Java. Für das Verständnis der Vorlesung sind Kenntnisse in einer objektorientierten Sprache notwendig. Datenstrukturen und Algorithmen Einführung Komplexität von Algorithmen (Folie 35, Seite 18 im Skript) Generische Typen Nur Konzepte aus der Vorlesung Programmierung. Mit einer Ausnahme. Java public class DoublePair { Double a; Double b; public Pair (Double a, Double b) {this.a = a; this.b = b; } public void setfirst(Double a) {this.a = a; } public void setsecond(Double b) {this.b = b; } public Double first() {return a; } public Double second() {return b; } public String toString () {return"[" + a + "," + b + "]"; } } Datenstrukturen und Algorithmen Einführung Komplexität von Algorithmen (Folie 36, Seite 18 im Skript) Generische Typen Nur Konzepte aus der Vorlesung Programmierung. Ausnahme: Generische Typen! Java public class Pair hA, Bi { protected A a; protected B b; public Pair (A a, B b) {this.a = a; this.b = b; } public void setfirst(A a) {this.a = a; } public void setsecond(B b) {this.b = b; } public A first() {return a; } public B second() {return b; } public String toString () {return"[" + a + "," + b + "]"; } } Datenstrukturen und Algorithmen Einführung Elementare Datenstrukturen Java public class Array hDi { protected D[ ] a; protected int size; Java public void set(int i, D d) { if(i ≥ size) { size = i + 1; realloc(); } a[i] = d; } (Folie 37, Seite 20 im Skript) Datenstrukturen und Algorithmen Einführung Elementare Datenstrukturen (Folie 38, Seite 20 im Skript) Java private void realloc() { if(size ≥ a.length) { D[ ] b = (D[ ]) new Object[2 ∗ size]; for(int i = 0; i < a.length; i ++) if(a[i] 6= null) b[i] = a[i]; a = b; } } Datenstrukturen und Algorithmen Einführung Elementare Datenstrukturen Java public D get(int i) { if(i ≥ size) return null; return a[i]; } Warum eine eigene Array-Klasse? 1 Erweiterbarkeit 2 Typsicherheit 3 Anfertigen von Statistiken (Folie 39, Seite 20 im Skript) Datenstrukturen und Algorithmen Einführung Elementare Datenstrukturen Java public interface MaphK , Di { public void insert(K k, D d); public void delete(K k); public D find(K k); public boolean iselement(K k); public int size(); public boolean isempty (); public Iterator hK , Di iterator (); public SimpleIterator hK i simpleiterator (); public Array hK i array (); public void print(); } (Folie 40, Seite 21 im Skript) Datenstrukturen und Algorithmen Einführung Elementare Datenstrukturen (Folie 41, Seite 21 im Skript) Assoziative Arrays Java public abstract class Dictionary hK , Di implements MaphK , Di { public abstract void insert(K k, D d); public abstract void delete(K k); public abstract Iterator hK , Di iterator (); public D find(K k) { Iterator hK , Di it; for(it = iterator (); it.more(); it.step()) { if (it.key ().equals(k)) { return it.data(); } } return null; } Datenstrukturen und Algorithmen Einführung Elementare Datenstrukturen (Folie 42, Seite 23 im Skript) Doppelt verkettete Listen Zusätzlicher Listenkopf Daten in Knoten gespeichert Zeiger zum Vorgänger und Nachfolger Zyklisch geschlossen – – 4 3 Geeignet für kleine assoziative Arrays. Für Adjazenzlistendarstellung sehr geeignet. 2 1 Datenstrukturen und Algorithmen Einführung Elementare Datenstrukturen (Folie 43, Seite 23 im Skript) Der Konstruktor muß den Listenkopf head erzeugen. Der Vorgänger und Nachfolger von head ist head selbst. – Java public class ListhK , Di extends Dictionary hK , Di { ListnodehK , Di head; public List() { head = new ListnodehK , Di(null, null); head.pred = head.succ = head; } Datenstrukturen und Algorithmen Einführung Elementare Datenstrukturen (Folie 44, Seite 23 im Skript) Java class ListnodehK , Di { K key ; D data; ListnodehK , Di pred, succ; Listnode(K k, D d) { key = k; data = d; pred = null; succ = null; } void delete() { pred.succ = succ; succ.pred = pred; } void copy (ListnodehK , Di n) { key = n.key ; data = n.data; } void append(ListnodehK , Di newnode) { newnode.succ = succ; newnode.pred = this; succ.pred = newnode; succ = newnode; } } Datenstrukturen und Algorithmen Einführung Elementare Datenstrukturen (Folie 45, Seite 23 im Skript) Java public void append(K k, D d) { head.pred.append(new ListnodehK , Di(k, d)); } Java public void prepend(K k, D d) { head.append(new ListnodehK , Di(k, d)); } Java public void delete(K k) { ListnodehK , Di n = findnode(k); if(n 6= null) n.delete(); } Datenstrukturen und Algorithmen Einführung Elementare Datenstrukturen (Folie 46, Seite 23 im Skript) Java public D find(K k) { ListnodehK , Di n = findnode(k); if(n ≡ null) return null; return n.data; } Java protected ListnodehK , Di findnode(K k) { ListnodehK , Di n; head.key = k; for(n = head.succ; !n.key .equals(k); n = n.succ); head.key = null; if(n ≡ head) return null; return n; } Datenstrukturen und Algorithmen Einführung Elementare Datenstrukturen (Folie 47, Seite 24 im Skript) Einfach verkettete Listen Kein Zeiger auf Vorgänger Einfachere Datenstruktur Operationen können komplizierter sein Frage: Wie kann ein Element vor einen gegebenen Knoten eingefügt werden? Ersetzen und alten Knoten anfügen! Vorsicht: Eine gefährliche Technik. Datenstrukturen und Algorithmen Einführung Elementare Datenstrukturen (Folie 48, Seite 25 im Skript) Listen – Laufzeit Manche Operation sind schnell, manche langsam. . . Operation Laufzeit append() Θ(1) delete () ∗ Θ(1) delete () Θ(n) find () Θ(n) insert () Θ(n) ∗ direktes Löschen eines Knotens, nicht über Schlüssel Datenstrukturen und Algorithmen Einführung Elementare Datenstrukturen (Folie 49, Seite 26 im Skript) Iteratoren Mittels eines Iterators können wir die Elemente einer Map aufzählen. Java public interface Iterator hK , Di { public void step(); public boolean more(); public K key (); public D data(); } Datenstrukturen und Algorithmen Einführung Elementare Datenstrukturen (Folie 50, Seite 26 im Skript) Iteratoren Mittels eines Iterators können wir die Elemente einer Map aufzählen. Java Iterator hK , Di it; for(it = map.iterator (); it.more(); it.step()) { System.out.println(it.key ()); } Datenstrukturen und Algorithmen Einführung Elementare Datenstrukturen (Folie 51, Seite 26 im Skript) Java public class StackhDi { private ListhObject, Di stack; private int size; public Stack() {stack = new ListhObject, Di(); size = 0; } public boolean isempty () {return size ≡ 0; } public D pop() { D x = stack.firstnode().data; stack.firstnode().delete(); size −−; return x; } public void push(D x) {stack.prepend(null, x); size ++; } public int size() {return size; } } Datenstrukturen und Algorithmen Einführung Elementare Datenstrukturen (Folie 52, Seite 26 im Skript) Java public class QueuehDi { private ListhObject, Di queue; private int size; public Queue() {queue = new ListhObject, Di(); size = 0; } public boolean isempty () {return size ≡ 0; } public D dequeue() { D x = queue.lastnode().data; queue.lastnode().delete(); size −−; return x; } public void enqueue(D x) {queue.prepend(null, x); size ++; } public int size() {return size; } } Datenstrukturen und Algorithmen Suchen und Sortieren Einfache Suche Lineare Suche Wir suchen x im Array a[0, . . . , n − 1] Algorithmus function find1 (int x) boolean : for i = 0 to n − 1 do if x = a[i] then return true fi od; return false Die innere Schleife ist langsam. (Folie 53, Seite 29 im Skript) Datenstrukturen und Algorithmen Suchen und Sortieren Einfache Suche (Folie 54, Seite 29 im Skript) Lineare Suche – verbessert Wir verwenden ein Sentinel-Element am Ende. Das Element a[n] muß existieren und unbenutzt sein. Algorithmus function find2 (int x) boolean : i := 0; a[n] := x; while a[i] 6= x do i := i + 1 od; return i < n Die innere Schleife ist sehr schnell. Datenstrukturen und Algorithmen Suchen und Sortieren Einfache Suche Lineare Suche – ohne zusätzlichen Platz Algorithmus function find3 (int x) boolean : if x = a[n − 1] then return true fi; temp := a[n − 1]; a[n − 1] := x; i := 0; while a[i] 6= x do i := i + 1 od; a[n − 1] := temp; return i < n − 1 Erheblicher Zusatzaufwand. Innere Schleife immer noch sehr schnell. (Folie 55, Seite 30 im Skript) Datenstrukturen und Algorithmen Suchen und Sortieren Einfache Suche (Folie 56, Seite 31 im Skript) Lineare Suche – Analyse Wieviele Vergleiche benötigt eine erfolgreiche Suche nach x im Mittel? Wir nehmen an, daß jedes der n Elemente mit gleicher Wahrscheinlichkeit gesucht wird. Es gilt Pr[x = a[i]] = 1/n für i ∈ {0, . . . , n − 1}. Sei C die Anzahl der Vergleiche: C = i + 1 gdw. x = a[i] Datenstrukturen und Algorithmen Suchen und Sortieren Einfache Suche (Folie 57, Seite 31 im Skript) Lineare Suche – Analyse Wir suchen den Erwartungswert von C . Pr[C = i] = 1/n für i = 1, . . . , n E (C ) = n X k Pr[C = k] = k=1 Es sind im Mittel (n + 1)/2 Vergleiche. n X k k=1 n+1 = n 2 Datenstrukturen und Algorithmen Suchen und Sortieren Einfache Suche Lineare Suche – Analyse Unser Ergebnis: Es sind im Mittel (n + 1)/2 Vergleiche. Ist das Ergebnis richtig? Wir überprüfen das Ergebnis für kleine n. n = 1? n = 2? (Folie 58, Seite 31 im Skript) Datenstrukturen und Algorithmen Suchen und Sortieren Einfache Suche Binäre Suche Wir suchen wieder x in a[0, . . . , n − 1]. Algorithmus function binsearch(int x) boolean : l := 0; r := n − 1; while l ≤ r do m := b(l + r )/2c; if a[m] < x then l := m + 1 fi; if a[m] > x then r := m − 1 fi; if a[m] = x then return true fi od; return false Wir halbieren den Suchraum in jedem Durchlauf. (Folie 59, Seite 32 im Skript) Datenstrukturen und Algorithmen Suchen und Sortieren Einfache Suche Binäre Suche Java public boolean binsearch(D d) { int l = 0, r = size − 1, m, c; while(l ≤ r ) { m = (l + r )/2; c = d.compareTo(get(m)); if(c ≡ 0) return true; if(c < 0) r = m − 1; else l = m + 1; } return false; } (Folie 60, Seite 32 im Skript) Datenstrukturen und Algorithmen Suchen und Sortieren Einfache Suche (Folie 61, Seite 32 im Skript) Binäre Suche – Analyse Java function binsearch(int x) boolean : l := 0; r := n − 1; while l ≤ r do m := b(l + r )/2c; if a[m] > x then l := m + 1 fi; if a[m] < x then r := m − 1 fi; if a[m] = x then return true fi od; return false Es sei n = r − l + 1 die Größe des aktuellen Unterarrays. Im nächsten Durchgang ist die Größe m − l oder r − m. Datenstrukturen und Algorithmen Suchen und Sortieren Einfache Suche Binäre Suche – Analyse Lemma Es sei a ∈ R und n ∈ N. Dann gilt 1 ba + nc = bac + n 2 da + ne = dae + n 3 b−ac = −dae (Folie 62, Seite 33 im Skript) Datenstrukturen und Algorithmen Suchen und Sortieren Einfache Suche (Folie 63, Seite 33 im Skript) Binäre Suche – Analyse Im nächsten Durchlauf ist die Größe des Arrays m − l oder r − m. Hierbei ist m = b(l + r )/2c. Die neue Größe ist also m − l = b(l + r )/2c − l = b(r − l)/2c = b(n − 1)/2c oder r − m = r − b(l + r )/2c = d(r − l)/2e = d(n − 1)/2e. Im schlimmsten Fall ist die neue Größe des Arrays d(n − 1)/2e. Datenstrukturen und Algorithmen Suchen und Sortieren Einfache Suche (Folie 64, Seite 33 im Skript) Rekursionsgleichung für binäre Suche Sei Sn die Anzahl der Schleifendurchläufe im schlimmsten Fall bei einer erfolglosen Suche. Wir erhalten die Rekursionsgleichung ( 0 Sn = 1 + Sd(n−1)/2e falls n < 1, falls n ≥ 1. Die ersten Werte sind: n Sn 0 0 1 1 2 2 3 2 4 3 5 3 6 3 Wir suchen eine geschlossene Formel für Sn . 7 3 8 4 Datenstrukturen und Algorithmen Suchen und Sortieren Einfache Suche (Folie 65, Seite 33 im Skript) Lösen der Rekursionsgleichung Wir betrachten den Spezialfall n = 2k − 1: l (2k − 1) − 1 m l 2k − 2 m = = d2k−1 − 1e = 2k−1 − 1. 2 2 Daher: S2k −1 = 1 + S2k−1 −1 für k ≥ 1 ⇒ S2k −1 = k + S20 −1 = k Datenstrukturen und Algorithmen Suchen und Sortieren Einfache Suche (Folie 66, Seite 34 im Skript) Binäre Suche – Analyse n Sn 0 0 1 1 2 2 3 2 4 3 5 3 6 3 7 3 8 4 Vermutung: S2k = S2k −1 + 1 Sn steigt monoton ⇒ Sn = k, falls 2k−1 ≤ n < 2k . Oder falls k − 1 ≤ log n < k. Dann wäre Sn = blog nc + 1. Datenstrukturen und Algorithmen Suchen und Sortieren Einfache Suche (Folie 67, Seite 34 im Skript) Binäre Suche – Analyse Wir vermuten Sn = blog nc + 1 für n ≥ 1. Induktion über n: S1 = 1 = blog 1c + 1 n > 1: Sn = 1 + Sd(n−1)/2e = 1 + logd(n − 1)/2e + 1. I.V. Noch zu zeigen: blog nc = logd(n − 1)/2e + 1 → Übungsaufgabe. Datenstrukturen und Algorithmen Suchen und Sortieren Einfache Suche (Folie 68, Seite 34 im Skript) Binäre Suche – Analyse Theorem Binäre Suche benötigt im schlimmsten Fall genau blog nc + 1 = log(n) + O(1) viele Vergleiche. Die Laufzeit ist O(log n).