Grundlagen der Algorithmen und Datenstrukturen Kapitel 7 Christian Scheideler + Helmut Seidl SS 2009 27.05.09 Kapitel 7 1 Wörterbuch S: Menge von Elementen Jedes Element e identifiziert über e.key(). Operationen: • S.insert(Element e): S = S [ {e}; • S.removeKey(Key k): S = Sn{e}; wobei e das Element ist mit e.key()==k • S.find(Key k): Falls es ein e2 S gibt mit e.key()==k, dann gib e aus, sonst gib ? aus 27.05.09 Kapitel 7 2 Suchstruktur S: Menge von Elementen Jedes Element e identifiziert über key(e). Operationen: • S.insert(Element e): S = S [ {e}; • S.removeKey(Key k): S = Sn{e}; wobei e das Element ist mit e.key()==k • S.locate(Key k): gib e2 S aus mit minimalem e.key() so dass e.key() ≥ k 27.05.09 Kapitel 7 3 Wörterbuch vs. Suchstruktur • Wörterbuch effizient über Hashing realisierbar (insert, remove und find kosten amortisiert / worst case O(1) Zeit) 1 3 14 5 5 10 3 19 14 1 19 10 • Hashing zerstört Ordnung und erlaubt daher keine effiziente locate-Operation 27.05.09 Kapitel 7 4 Suchstruktur Erste Lösung: sortierte Liste (mit Wächter) null 3 … 19 42 Problem: insert, remove, locate kosten im worst case Θ (n) Zeit Einsicht: Wenn locate effizient zu implementieren wäre, dann auch alle anderen Operationen 27.05.09 Kapitel 7 5 Suchstruktur Idee: füge Navigationsstruktur hinzu, die locate effizient macht Navigationsstruktur null 27.05.09 3 … Kapitel 7 19 42 6 Binärer Suchbaum (ideal) 10 3 1 1 27.05.09 19 5 3 5 14 10 Kapitel 7 14 28 19 28 7 Binärer Suchbaum Suchbaum-Regel: k T1 Für alle Schlüssel k´ in T1 und k´´ in T2: k´ < k < k´´ T2 Damit locate Operation einfach zu implementieren. 27.05.09 Kapitel 7 8 locate(k) Operation k T1 T2 Für alle Schlüssel k´ in T1 und k´´ in T2: k´< k < k´´ Locate-Strategie: • Starte in Wurzel des Suchbaums • Für jeden erreichten Knoten v: – Falls key(v) > k, gehe zum linken Kind von v, sonst gehe zum rechten Kind 27.05.09 Kapitel 7 9 Binärer Suchbaum Formal: für einen Baumknoten v sei • key(v) der Schlüssel in v • d(v) die Anzahl Kinder von v • Suchbaum-Invariante: (s.o.) • Grad-Invariante: Alle Baumknoten haben max. zwei Kinder • Schlüssel-Invariante: Für jedes Element e in der Liste gibt es genau einen Baumknoten v mit key(v) == e.key(). 27.05.09 Kapitel 7 10 locate(9) 10 3 1 1 27.05.09 19 5 3 5 14 10 Kapitel 7 14 28 19 28 11 Insert und Remove Operationen Strategie: • insert(e): Suche einen Knoten entweder mit e.key() < key und linkem Nachfolger null oder mit e.key() > key und rechtem Nachfolger null. Füge für e ein neues Suchbaumblatt ein, so dass Suchbaum-Regel erfüllt ist. • removeKey(k): Suche einen Knoten mit key == k. Lösche den Inhalt des Knoten. Hat er keine Nachfolger, lösche den ganzen Knoten. Hat er einen Nachfolger, verkürze. Hat er zwei Nachfolger, überschreibe ihn mit dem größten Knoten des linken Nachfolgers. 27.05.09 Kapitel 7 12 Insert(5) 10 14 1 28 1 27.05.09 10 Kapitel 7 14 28 13 Insert(5) 10 14 1 5 1 27.05.09 5 28 10 Kapitel 7 14 28 14 Insert(12) 10 14 1 5 1 27.05.09 5 28 10 Kapitel 7 14 28 15 Insert(12) 10 14 1 5 1 27.05.09 5 12 10 Kapitel 7 12 28 14 28 16 removeKey(1) 10 14 1 5 1 27.05.09 5 12 10 Kapitel 7 12 28 14 28 17 removeKey(1) 10 14 5 5 27.05.09 12 10 Kapitel 7 12 28 14 28 18 removeKey(14) 10 14 5 5 27.05.09 12 10 Kapitel 7 12 28 14 28 19 removeKey(14) 10 14 5 5 27.05.09 12 10 Kapitel 7 12 28 14 28 20 removeKey(14) 10 12 5 5 27.05.09 28 10 Kapitel 7 12 28 21 Datenstruktur // Item: Typ für , TreeItem: Typ für interface Elem<K extends Comparable<K>> { K key();} class Item<K extends Comparable<K>, E extends Elem<K>> { // doppelt verketttete Liste von Elementen und Baumknoten TreeItem<K,E> t = null; E e = null; Item<K,E> prev, next; Item () { prev = next = this;} Item (E e, TreeItem<K,E> t, Item<K,E> prev, Item<K,E> next) { this.e = e; this.t = t; this.prev = prev; this.next = next;} e Item<K,E> insertBefore(E e, TreeItem<K,E> t) {...} Item<K,E> insertAfter(E e, TreeItem<K,E> t) {...} void remove() {...} } 27.05.09 Kapitel 7 22 Datenstruktur class TreeItem<K extends Comparable<K>, E extends Elem<K>> { // Baumknoten mit Schlüssel, Item, Vater und Nachfolgern K key; Item<K,E> item = null; TreeItem<K,E> pa = null; TreeItem<K,E> lc = null; TreeItem<K,E> rc = null; TreeItem(K key,TreeItem<K,E> pa) { this.key = key; this.pa = pa; } ... } 27.05.09 Kapitel 7 k e 23 Datenstruktur class SearchTree<K extends Comparable<K>, E extends Elem<K>> { // erzeuge leere doppeltverkettete Liste Item<K,E> l; TreeItem<K,E>; SearchTree() { l = new Item<K,E>(); t = new TreeItem(null,null); t.item = l; l.t = t; } ... // leerer Baumknoten mit // Verweis auf leeren Listenknoten } 27.05.09 Kapitel 7 24 Locate Operation // in SearchTree Item<K,E> locate(K key) { if (t.rc == null) return l; return t.rc.locate(key); // starte Suche in Wurzel } // in TreeItem Item<K,E> locate(K key) { switch (key.compareTo (this.key)) { case -1: if (lc == null) return item; else return lc.locate(key); case 0: return item; case +1: if (rc == null) return item.next; return rc.locate(key); } return null; } 27.05.09 Kapitel 7 25 Insert Operation // in SearchTree void insert(Element e) { K key = e.key(); if (t.rc == null) { t.rc = new TreeItem<K,E> (key,t); t.rc.item = l.insertAfter(e, t.rc); } else t.rc.insert(e); } 27.05.09 Kapitel 7 26 Insert Operation // in TreeItem void insert (E e) { K key = e.key(); if (key.compareTo(this.key) < 0) if (lc == null) { lc = new TreeItem<K,E>(key,this); lc.item = item.insertBefore(e,lc); } else lc.insert(e); else if (rc == null) { rc = new TreeItem<K,E>(key,this); rc.item = item.insertAfter(e,rc); } else rc.insert(e); } 27.05.09 Kapitel 7 27 RemoveKey Operation // in SearchTree void removeKey(K key) { if (t.rc == null) return; t.rc. removeKey(key); } // in TreeItem void removeKey(K key) { switch (key.compareTo(this.key)) { case -1: if (lc == null) break; else lc.removeKey(key); break; case +1: if (rc == null) break; else rc.removeKey(key); break; ... 27.05.09 Kapitel 7 28 RemoveKey Operation ... case 0: item.remove(); if (lc == null) if (this == pa.lc) pa.lc = rc; else pa.rc = rc; if (rc!= null) rc.pa = pa; } else if (rc == null) { if (this == pa.lc) pa.lc = lc; else pa.rc = lc; lc.pa = pa; } else lc.removeMax(this); } // Hochkopieren des Nachfolgers // Hochkopieren des Nachfolgers // Korrektur des Vaterverweises } 27.05.09 Kapitel 7 29 RemoveKey Operation // in TreeItem void removeMax(TreeItem<K,E> t) { if (rc == null) { item.t = t; t.item = item; t.key = item.e.key(); if (this == pa.lc) { pa.lc = lc; if (lc != null) lc.pa = pa; } else { pa.rc = lc; if (lc != null) lc.pa = pa; } } else rc.removeMax(t); } 27.05.09 Kapitel 7 30 Binärbaum Problem: Binärbaum kann entarten! Beispiel: Zahlen werden in sortierter Folge eingefügt 1 3 locate() benötigt Θ (n) Zeit im worst case 5 10 14 19 28 1 27.05.09 3 5 10 14 19 Kapitel 7 28 31 (a,b)-Bäume Problem: Binärbaum kann entarten! Lösung: (a,b)-Baum Idee: • Alle Knoten v außer der Wurzel enthalten d(v)-1 Schlüssel mit a ≤ d(v) ≤ b, wobei a ≥ 2 und b ≥ 2a-1 ist • Alle Blätter sind in derselben Ebene 27.05.09 Kapitel 7 32 (a,b)-Bäume Formal: für einen Baumknoten v sei • d(v)-1 die Anzahl der Schlüssel in v • t(v) die Tiefe von v (Wurzel hat Tiefe 0) • Form-Invariante: Für alle Blätter v,w: t(v)=t(w) • Grad-Invariante: Für alle inneren Knoten v außer Wurzel: d(v) 2 [a,b], für Wurzel r: d(r) 2 [2,b] (sofern #Elemente >1) 27.05.09 Kapitel 7 33 (a,b)-Bäume Lemma 7.1: Ein (a,b)-Baum für n Elemente hat Tiefe max. 1+loga (n/2) Beweis: • Die Wurzel hat Grad ≥ 2 und jeder andere innere Knoten hat Grad ≥ a. • Bei Tiefe t gibt es mindestens 2at-1 Blätter • n ≥ 2at-1 , t ≤ 1+loga n/2 27.05.09 Kapitel 7 34 (a,b)-Bäume (a,b)-Suchbaum-Regel: s1, s2,…,sd-1 T1 T2 .... Für alle Schlüssel k in Ti und k´ in Ti+1: k < si < k´ Td Damit locate Operation einfach zu implementieren. 27.05.09 Kapitel 7 35 Locate(9) 10 14 19 28 1 3 5 1 27.05.09 3 5 10 14 Kapitel 7 19 28 36 Insert(e) Operation Strategie: • Suche Blatt v, in das e.key() gehört. Finde benachbartes Element e´ in dem Blatt. Falls e'.key()>e.key(), füge e vor e´ ein, ansonsten dahinter. ... 27.05.09 e‘ Kapitel 7 ... 37 Insert(e) Operation Strategie: • Suche Blatt v, in das e.key() gehört. Finde benachbartes Element e´ in dem Blatt. Falls e'.key()>e.key(), füge e vor e´ ein, ansonsten dahinter. ... 27.05.09 e Kapitel 7 e‘ ... 38 Insert(e) Operation • Füge e.key() und Verweis auf e in Baumknoten v ein. Falls v nachher weniger als b Elemente hat, dann fertig. v ... 27.05.09 x v …x z… y z e e’ ... ... Kapitel 7 x …x y z… y z e e’ ... 39 Insert(e) Operation • Falls v nachher b Elemente enthält, teile v in zwei Knoten auf. (Beispiel: a=2, b=4) w … a u' b … … a b… v x 27.05.09 x u u‘ y e e’ u u‘ xu y e y z x Kapitel 7 u e’ u‘ y z 40 Insert(e) Operation • Falls Grad von w größer als b, dann teile w in zwei Knoten auf (usw, bis Grad ≤ b oder Wurzel aufgeteilt wurde) … z … w 27.05.09 w x y x y z t Kapitel 7 t 41 Insert(8) a=2, b=4 14 19 28 42 3 5 10 3 27.05.09 5 10 14 Kapitel 7 19 28 42 42 Insert(8) a=2, b=4 14 19 28 42 3 5 8 10 3 27.05.09 5 8 10 Kapitel 7 14 19 28 42 43 Insert(8) a=2, b=4 8 14 3 27.05.09 19 28 42 10 3 5 5 8 10 Kapitel 7 14 19 28 42 44 Insert(32) a=2, b=4 8 14 3 27.05.09 19 28 42 10 3 5 5 8 10 Kapitel 7 14 19 28 42 45 Insert(32) a=2, b=4 8 14 3 27.05.09 19 28 32 42 10 3 5 5 8 10 Kapitel 7 14 19 28 32 42 46 Insert(32) a=2, b=4 8 14 32 10 3 5 3 27.05.09 5 8 10 Kapitel 7 3 5 14 19 42 28 32 42 47 Insert Operation • Form-Invariante: Für alle Blätter v,w: t(v)=t(w) Erfüllt durch insert! • Grad-Invariante: Für alle inneren Knoten v außer Wurzel: d(v) 2 [a,b], für Wurzel r: d(r) 2 [2,b] 1) Insert splittet Knoten mit b Schlüsseln in Knoten mit b/2 bzw. (b-1)/2 Schlüsseln. Wenn b ≥ 2a-1, dann beide Werte ≥ a-1. 2) Wenn Wurzel b Schlüssel enthält, wird eine neue Wurzel mit einem Schlüssel d.h. Grad 2 erzeugt. 27.05.09 Kapitel 7 48 removeKey(k) Operation Strategie: • Erst locate(k) bis Element e in Liste erreicht. Falls e.key()==k, entferne e aus Liste, ansonsten stop. ... 27.05.09 e´ e Kapitel 7 e´´ ... 1 49 RemoveKey(k) Operation Strategie: • Erst locate(k) bis Element e in Liste erreicht. Falls e.key()==k, entferne e aus Liste, ansonsten stop. ... 27.05.09 e´ e´´ Kapitel 7 ... 1 50 RemoveKey(k) Operation • Entferne Verweis auf e und Schlüssel k vom Baumknoten v mit e. Falls v ein Blatt ist und noch a-1 Schlüssel enthält, dann fertig. v ... x v …x k y… k y ... ... …x y… x y ... e 27.05.09 Kapitel 7 51 RemoveKey(k) Operation • Falls d(v) < a und direkter Nachbar von v hat Grad > a Schlüssel, nimm Kante von diesem Nachbarn. (Beispiel: a=2, b=4) u k z u r z v k r s t x 27.05.09 k r s t x Kapitel 7 s k r t s t 52 RemoveKey(k) Operation • Falls d(v) < a und kein direkter Nachbar von v hat Grad >a, merge v mit Nachbarn. (Beispiel: a=3, b=5) u y t u t v x x 27.05.09 r s y r x y r s s t x Kapitel 7 y r s t 53 Remove(k) Operation • Veränderungen hoch bis Wurzel, und Wurzel hat Grad <2: entferne Wurzel. x y z 27.05.09 x y z Kapitel 7 54 Remove(k) Operation • Falls y in innerem Knoten gelöscht wird, ersetze durch max. Schlüssel in linkem Teilbaum. (Beispiel: a=2, b=4) u z t x y x 27.05.09 u y t x r s y r s t x Kapitel 7 r s y r s t 55 Remove(10) a=2, b=4 10 19 14 1 3 5 1 27.05.09 3 5 10 Kapitel 7 14 28 19 28 56 Remove(10) a=2, b=4 19 14 1 3 5 1 27.05.09 3 5 14 Kapitel 7 28 19 28 57 Remove(10) a=2, b=4 5 19 14 1 3 1 27.05.09 3 5 14 Kapitel 7 28 19 28 58 Remove(14) a=2, b=4 5 19 14 1 3 1 27.05.09 3 5 14 Kapitel 7 28 19 28 59 Remove(14) a=2, b=4 5 19 28 1 3 1 27.05.09 3 5 19 Kapitel 7 28 60 Remove(14) a=2, b=4 3 19 1 1 27.05.09 5 3 28 5 19 Kapitel 7 28 61 Remove(3) a=2, b=4 3 19 1 1 27.05.09 5 3 28 5 19 Kapitel 7 28 62 Remove(3) a=2, b=4 1 19 5 1 27.05.09 28 5 19 Kapitel 7 28 63 Remove(3) a=2, b=4 19 1 5 1 27.05.09 28 5 19 Kapitel 7 28 64 Remove(1) a=2, b=4 19 1 5 1 27.05.09 28 5 19 Kapitel 7 28 65 Remove(1) a=2, b=4 19 5 28 5 27.05.09 19 Kapitel 7 28 66 Remove Operation • Form-Invariante: Für alle Blätter v,w: t(v)=t(w) Erfüllt durch Remove! • Grad-Invariante: Für alle inneren Knoten v außer Wurzel: d(v) 2 [a,b], für Wurzel r: d(r) 2 [2,b] 1) Remove verschmilzt Knoten mit Grad a-1 mit Knoten mit Grad a. Wenn b ≥ 2a-1, dann hat resultierender Knoten Grad ≤ b. 2) Remove verschiebt Kante von Knoten mit Grad >a nach Knoten mit Grad a-1. Auch OK. 3) Wurzel gelöscht: Kinder vorher verschmolzen, Grad vom verbleibenden Kind ≥ a (und ≤ b), also auch OK. 27.05.09 Kapitel 7 67 Mehr Operationen • min/max Operation: Verwende die first und last Operationen, um das kleinste oder größte Element auszugeben. Zeit O(1). • Bereichsanfragen: Um alle Elemente im Bereich [x,y] zu suchen, führe locate(x) aus und durchlaufe dann die Liste, bis ein Element >y gefunden wird. Zeit O(log n + Ausgabegröße). 27.05.09 Kapitel 7 68 Mehr Operationen • Konkatenation: Ziel: Verknüpfe zwei (a,b)-Bäume T1 und T2 mit s1 und s2 Elementen zu (a,b)-Baum T (Schlüssel in T1 < Schlüssel in T2) T1 27.05.09 + = T2 Kapitel 7 T 69 Mehr Operationen • Konkatenation: Strategie (s1 ≤ s2): k… T1 … 27.05.09 k k T2 <a Kanten: wie remove-Op behandeln … Kapitel 7 >b Kanten: wie insert-Op behandeln … 70 Mehr Operationen • Aufspaltung: Ziel: Spalte (a,b)-Baum T in (a,b)-Bäume T1 und T2 bei Schlüssel k auf. T … 27.05.09 k T1 k´ … … Kapitel 7 T2 k k´ 71 Split-Operation 1. Suche nach k Pfad nach k v1 : Suchpfad v2 v3 ... vk … 27.05.09 k k´ Kapitel 7 … 72 Split-Operation 2. Aufspaltung entlang Suchpfad s1 … si si+1 … sd-1 : Suchpfad s1 … si si+1 … sd-1 links rechts k s1 … k si … sd-1 … 27.05.09 links s1 … k Kapitel 7 rechts si … sd-1 73 Split-Operation 2. Abbruch bei Aufspaltung: Fall 1: Fall 2: s1>k: Aufspals… tung hier been- 1 den ….sd-1 k 27.05.09 sd-1<k: Aufspaltung hier beenden k Kapitel 7 74 Split-Operation 1. Gradreparatur in den beiden Teilbäumen Strategie: bottom-up entlang Suchpfad, um zu gültigen (a,b)-Bäumen zurückzukehren. (Wie bei insert und remove Operationen.) 27.05.09 Kapitel 7 75 Split(1) a=2, b=4 10 19 14 17 1 3 5 1 27.05.09 3 5 10 14 Kapitel 7 28 17 19 28 76 Split(1) 10 19 1 1 27.05.09 3 3 14 17 5 5 10 14 Kapitel 7 17 28 19 28 77 Split(5) a=2, b=4 10 19 14 17 1 3 5 1 27.05.09 3 5 10 14 Kapitel 7 28 17 19 28 78 Split(5) 10 19 14 17 1 3 5 1 27.05.09 3 5 10 14 Kapitel 7 17 28 19 28 79 Split(5) 14 19 10 1 3 5 1 27.05.09 3 5 10 17 14 Kapitel 7 17 28 19 28 80 Split(10) a=2, b=4 10 19 14 17 1 3 5 1 27.05.09 3 5 10 14 Kapitel 7 17 28 19 28 81 Split(10) 10 19 14 17 1 3 5 1 27.05.09 3 5 10 14 Kapitel 7 17 28 19 28 82 Split(10) 5 19 1 3 1 27.05.09 14 17 10 3 5 10 14 Kapitel 7 17 28 19 28 83 n Update-Operationen Theorem: Es gibt eine Folge von n insert und removeKey Operationen im (2,3)Baum, so dass Gesamtanzahl der split und merge Operationen Ω (n log n) ist. Beweis: Übung. 27.05.09 Kapitel 7 84 n Update-Operationen Theorem 7.3: Betrachte einen (a,b)-Baum mit b ≥ 2a, der anfangs leer ist. Für jede Folge von n insert und remove Operationen ist die Gesamtanzahl der split und merge Operationen O(n). Beweis: Amortisierte Analyse (wird nicht in Vorlesung behandelt) 27.05.09 Kapitel 7 85 Zusammenfassung • Binäre Suchbäume • (a,b)-Bäume • Zusätzliche Operationen • Weiter mit Graphrepräsentationen (Kapitel 8) 27.05.09 Kapitel 7 86 Externer (a,b)-Baum Prozessor Interner Speicher Größe M Blockgröße B Externer Speicher 27.05.09 Kapitel 7 87 Externer (a,b)-Baum Problem: Minimiere Blocktransfers zwischen internem und externem Speicher Lösung: • verwende b=B (Blockgröße) und a=b/2 • halte oberste loga(M/b) Ebenen des (a,b)-Baums im internen Speicher (Speicher <= M) • Lemma 7.1: Tiefe des (a,b)-Baums max.1+bloga (n+1)/2c • loga[(n+1)/2] - loga(M/b) = loga[(n+1)/(2M)] + logab • logab = O(1) • Kosten für insert, remove und locate Operationen: O(logB(n/M)) Blocktransfers 27.05.09 Kapitel 7 88