Liste Bäume AVL-Baum B-Baum Optimale Bäume Trie kd-Baum 3. Dynamische Datenstrukturen 3.1 Listen (vgl. Informatik I) public class List<K extends Comparable<K>,D> implements MyCollection<K,D>{ protected static class ListElem<K,E> { ... s.u. ...} public class ListIterator implements Iterator<Pair<K,D>>{ ... s.u. ...} protected ListElem<K,D> head = null; public ListIterator iterator(){return new ListIterator();} public void insert(K key, D cont){ head = new ListElem<K,D>(kex, cont, head);} public D find(K key) throws Exception{ for(Pair<K,D> val: this) if (val.getFirst().equals(key)) return val.getSecond(); throw new Exception(key+" nicht gefunden");} public void delete(K key){ ... s.u. ...} } 51 Liste Bäume AVL-Baum B-Baum Optimale Bäume Trie kd-Baum Innere Klasse ListElem protected static class ListElem<K,E> { ListElem<K,E> succ; K key; E content; ListElem(K ky, E cont, ListElem<K,E> next){ succ = next; key = ky; content = cont;} } • Klasse Pair<K,T> siehe Webseite 52 Liste Bäume AVL-Baum B-Baum Optimale Bäume Trie kd-Baum Innere Klasse ListIterator public class ListIterator implements Iterator<Pair<K,D>>{ protected ListElem<K,D> current; public ListIterator(){current = head;} public boolean hasNext(){return (current != null);} public Pair<K,D> next(){ Pair<K,D> val = new Pair<K,D>(current.key, current.content); current = current.succ; return val;} public void remove(){ if (head.succ == current){head = current; return;} ListElem<K,D> elem = head; while (elem.succ.succ != current) elem = elem.succ; elem.succ = current;} } 53 Liste Bäume AVL-Baum B-Baum Optimale Bäume Trie kd-Baum Löschen public void delete(K key){ ListIterator iter = iterator(); Pair<K,D> value; while (iter.hasNext()){ value = iter.next(); if (value.getFirst().equals(key)) iter.remove();}} Aufwand der Listenoperationen: • für insert: O(1) (bei Vermeiden von Duplikaten: O(n)) • für find, delete: O(n) 54 Liste Bäume AVL-Baum B-Baum Optimale Bäume Trie kd-Baum 3.2 Baumstrukturen 3.2.1 Binärer Suchbaum Idee: für Schlüssel k in Wurzel gilt: • k0 > k ∀k 0 im rechten Teilbaum • k0 ≤ k ∀k 0 im linken Teilbaum 43 24 16 78 39 42 55 Liste Bäume AVL-Baum B-Baum Optimale Bäume Trie kd-Baum Operationen im binären Suchbaum 43 43 24 24 78 42 78 insert(29,d) 16 16 39 42 24 78 delete(43) 39 29 16 42 39 29 • Operationen: Suchen, Einfügen, Löschen • Ziel: Aufwand (im Mittel) O(log n) bei n Schlüsseln • weitere Operationen: sortierte Ausgabe (eines Teils) der Schlüssel 56 Liste Bäume AVL-Baum B-Baum Optimale Bäume Trie kd-Baum Binärer Suchbaum in Java public class Tree<K extends Comparable<K>,D> implements MyCollection<K,D>{ protected Node<K,D> root = null; public void insert(K key, D data){ if (root == null) root = new Node<K,D>(key, data, null, null); else root.insertNode(key, data);} public D find(K key) throws Exception { if (root == null) throw new Exception("nicht gefunden"); else return root.findNode(key);} public void delete(K key) { if (root != null) root = root.deleteNode(key);} public LWR<K,D> iterator(){return new LWR<K,D>(root);} } 57 Liste Bäume AVL-Baum B-Baum Optimale Bäume Trie kd-Baum Klasse Node class Node<K extends Comparable<K>,D> { K key; D data; Node<K,D> left; Node<K,D> right; Node(K k, D d, Node<K,D> l, Node<K,D> r){ key=k; data=d; left=l; right=r;} public void insertNode(K k, D data){ ... s.u. ...} public D findNode(K k) throws Exception { ... s.u. ...} private Node<K,D> findMaxPred(){ ... s.u. ...} public Node<K,D> deleteNode(K k){ ... s.u. ...} } 58 Liste Bäume AVL-Baum B-Baum Optimale Bäume Trie kd-Baum Klasse Node: Einfügen public void insertNode(K k, D data) { if (key.compareTo(k)<0) if (right == null) right = new Node<K,D>(k, data, null, null); else right.insertNode(k, data); else if (left == null) left = new Node<K,D>(k, data, null, null); else left.insertNode(k, data);} 59 Liste Bäume AVL-Baum B-Baum Optimale Bäume Trie kd-Baum Klasse Node: Suchen public D findNode(K k) throws Exception { if (key.compareTo(k)<0) if (right == null) throw new Exception(k+" nicht gefunden"); else return right.findNode(k); else if (k.compareTo(key)<0) if (left == null) throw new Exception(k+" nicht gefunden"); else return left.findNode(k); else return data;} 60 Liste Bäume AVL-Baum B-Baum Optimale Bäume Trie kd-Baum Klasse Node: Löschen private Node<K,D> findMaxPred() { if (right.right == null) return this; else return right.findMaxPred();} public Node<K,D> deleteNode(K k){ if (key.compareTo(k)<0){ if (right != null) right = right.deleteNode(k); return this;} else if (k.compareTo(key)<0) { if (left != null) left = left.deleteNode(k); return this;} if (left == null) return right; if (right == null) return left; if (left.right == null) {left.right = right; return left;} Node<K,D> maxPred = left.findMaxPred(); Node<K,D> max = maxPred.right; maxPred.right = max.left; max.left = left; return max;} max.right = right; 61 Liste Bäume AVL-Baum B-Baum Optimale Bäume Trie kd-Baum Iterator für Binärbaum import java.util.*; class LWR<K extends Comparable<K>,D> implements Iterator<Pair<K,D>>{ protected Stack<Node<K,D>> stack = new Stack<Node<K,D>>(); public LWR(Node<K,D> root){pushSpine(root);} private void pushSpine(Node<K,D> current){ while (current != null){ stack.push(current); current = current.left;}} public boolean hasNext(){return ! stack.empty();} public Pair<K,D> next(){ Node<K,D> current = stack.pop(); if (current.right != null) pushSpine(current.right); return new Pair<K,D>(current.key,current.data);} public void remove(){throw new UnsupportedOperationException();} } • analog: Durchlauf in Reihenfolge WLR (Präfix), LRW (Postfix), . . . 62 Liste Bäume AVL-Baum B-Baum Optimale Bäume Trie kd-Baum Aufwand im schlechtesten Fall • im schlechtesten Fall degeneriert der Baum zur Liste 1 2 .. . n find (n), t insert (n), t delete (n) ∈ O(n) • daher: tW W W 63 Liste Bäume AVL-Baum B-Baum Optimale Bäume Trie kd-Baum Aufwand im Mittel • o.B.d.A. Schlüssel 1, . . . , n • Annahme: alle Permutationen als Eingabefolge gleich wahrscheinlich • ignoriere Löschen; beachte: • delete löscht vorzugsweise links • also: „Balance“ wird verschlechtert • aber: „faireres“ delete leicht möglich 64 Liste Bäume AVL-Baum B-Baum Optimale Bäume Trie kd-Baum Mittlere Pfadlänge wn bei Suche (i) • mittlere Pfadlänge wn , wenn i zuerst eingegeben: (i) wn = 1 i −1 n−i ·0+ · (wi−1 + 1) + · (wn−i + 1) n n n • für beliebiges zuerst eingegebenes Element: w0 = 0 wn = = = = 1 n · n P 1 n · i=1 n P i=1 (i) für n ≥ 1 wn ( i−1 n · wi−1 + n−1 n + n−1 n + 1 n2 2 n2 · n P n−i n · wn−i + n−1 n ) ((i − 1) · wi−1 + (n − i) · wn−i ) i=1 n−1 P i · wi i=1 65 Liste Bäume AVL-Baum B-Baum Optimale Bäume Trie kd-Baum Mittlere Pfadlänge wn bei Suche (2) zeige per Induktion: wn ≤ 4 · log2 n für n ≥ 1 √ n = 1: w1 = 0 = 4 · log2 1 1, . . . , n − 1 → n, n > 1 : wn = ≤ = ≤ n−1 n + n−1 n + n−1 n + n−1 n + 2 n2 8 n2 n−1 P i=1 n−1 P i · wi i · log2 i i=1 bn/2c P 8 ( n2 i=1 i · log2 i + 8 (log2 n2 n2 n−1 P i · log2 i) i=bn/2c+1 · bn/2c P i=1 i + log2 n · n−1 P i) i=bn/2c+1 . . . (b.w.) 66 Liste Bäume AVL-Baum B-Baum Optimale Bäume Trie kd-Baum Mittlere Pfadlänge wn bei Suche (3) wn 8 (log2 n2 n2 · bn/2c P + 8 (log2 n2 n2 · i=1 b n2 c(b n2 c+1) 2 n−1 n + 8 ((log2 n2 = n−1 n − n n 8 b 2 c(b 2 c+1) 2 n2 ≤ n−1 n − n 8· n−1 2 ·2 n2 ·2 n−1 n + = n−1 n = ≤ = 4 · log2 n i + log2 n · n − 1) · + + i) i=bn/2c+1 log2 n · ( (n−1)n 2 b n2 c(b n2 c+1) 2 8 n2 n−1 P log2 n · − b n2 c(b n2 c+1) )) 2 + log2 n · ( (n−1)n − 2 b n2 c(b n2 c+1) )) 2 (n−1)n 2 + 4 · log2 n 2 67 Liste Bäume AVL-Baum B-Baum Optimale Bäume Trie kd-Baum Mittlerer Aufwand für das Suchen, Einfügen und Löschen • aus wn ≤ 4 · log2 n folgt: tafind (n) ≤ 4 · log2 n ∈ O(log n) • bei genauerer Rechnung: tafind (n) ≈ 1.386 · log2 n (vgl. Güting,Dieker, S. 137-141) • analog: tAinsert (n) ∈ O(log n), tAdelete (n) ∈ O(log n) 68 Liste Bäume AVL-Baum B-Baum Optimale Bäume Trie kd-Baum Variante: inhomogener Suchbaum • Daten nur in Blättern; innere Knoten enthalten nur Schlüssel • größenordnungsmäßiger Aufwand der Operationen unverändert • Löschen einfacher • innere Knoten benötigen weniger Speicher → interessant bei knappem Hauptspeicher (Blätter ggf. auf Sekundärspeicher) Beispiel: 42 27 (16, "Bob") (72, "Jim") (42, "Sam") 69 Liste Bäume AVL-Baum B-Baum Optimale Bäume Trie kd-Baum 3.2.2 Balancierte Suchbäume • Ziel: auch im schlechtesten Fall logarithmischer Aufwand für Suchen, Einfügen, Löschen • Idee: Baum bei Einfügen und Löschen „geeignet ausbalancieren“ • Ansätze • höhenbalancierte Bäume (z. B. AVL) • gewichtsbalancierte Bäume • B-Baum(-Variante) 70 Liste Bäume AVL-Baum B-Baum Optimale Bäume Trie kd-Baum 3.2.2.1 AVL-Bäume • nach Adelson-Velskii, Landis (1962) • höhenbalancierter, binärer Suchbaum • Idee: für jeden Knoten unterscheiden sich die Höhen der Teilbäume um ≤ 1 71 Liste Bäume AVL-Baum B-Baum Optimale Bäume Trie kd-Baum Berechnung der maximalen Höhe • konstruiere zu vorgegebener Höhe einen AVL-Baum mit minimaler Knotenanzahl Menge Fh der Fibonacci-Bäume der Höhe h: • ({v }, ∅) ∈ F0 • ({v1 , v2 }, {(v1 , v2 )}) ∈ F1 • T1 = (V1 , E1 ) ∈ Fh−1 , T2 = (V2 , E2 ) ∈ Fh−2 ⇒ ({v } ∪ V1 ∪ V2 , E1 ∪ E2 ∪ {(v , v1 ), (v , v2 )}) ∈ Fh falls h ≥ 2, v1 Wurzel von T1 , v2 Wurzel von T2 • keine anderen Bäume sind Fibonacci-Bäume 72 Liste Bäume AVL-Baum B-Baum Optimale Bäume Trie kd-Baum Fibonacci-Bäume Minimale Knotenanzahl eines AVL-Baums #Knoten k (h) von t ∈ Fh : k (0) = 1 k (1) = 2 k (h) = k (h − 1) + k (h − 2) + 1 für h ≥ 2 • k (h) ist die minimale Knotenanzahl eines AVL-Baums der Höhe h 73 Liste Bäume AVL-Baum B-Baum Optimale Bäume Trie kd-Baum Vergleich: Fibonacci-Zahlen und k (h) fib(0) = 0 fib(1) = 1 fib(h) = fib(h − 1) + fib(h − 2) für h ≥ 2 h 0 1 2 3 4 5 6 7 8 9 10 fib(h) 0 1 1 2 3 5 8 13 21 34 55 k(h) 1 2 4 7 12 20 33 54 88 143 n ≥ k (h) = fib(h + 3) − 1 > √ ⇒ 5 · (n + 2) > Φh+3 √ ⇒ logΦ ( 5(n + 2)) − 3 > h h+3 Φ√ 5 −2 (mit Φ = 232 √ 1+ 5 2 , s. Knuth) ⇒ h ∈ O(log n) genauer: h ≤ 1.44 · log2 n + 1 find (n) ∈ O(log n), da Suchen wie im binären Suchbaum damit: tW 74 Liste Bäume AVL-Baum B-Baum Optimale Bäume Trie kd-Baum Einfügen in AVL-Baum • beim Einfügen eines Knotens kann die Balance eines Knotens unzulässig werden • Balance durch eine lokale Reorganisation reparabel • (bis auf Symmetrie) zwei unterschiedliche Situationen y x h(T1)+2 −2 x −1 T3 y y h(T3)+3 1 T4 z −1 oder 1 T1 T2 T3 0 h(T1)+1 T1 T2 x −2 h(T2)+3 oder fertig! Rechtsrotation(y) T2 T1 0 z Doppelrotation links−rechts(x) = Linksrotation(y) ° Rechtsrotation(x) 0 y −1 oder 0 T1 T2 T3 x T3 0 oder 1 h(T2)+2 oder h(T3)+2 T4 75 Liste Bäume AVL-Baum B-Baum Optimale Bäume Trie kd-Baum Beispiel: AVL-Baum-Operationen 42 42 insert(28) 27 16 58 29 35 62 29 27 37 58 35 16 28 37 29 28 insert(4) 27 16 4 delete(29) 42 28 62 35 16 58 37 4 62 42 27 35 58 37 62 76 Liste Bäume AVL-Baum B-Baum Optimale Bäume Trie kd-Baum AVL-Baum in Java public class AVL<K extends Comparable<K>,D> implements MyCollection<K,D>{ class Node{ // member class K key; D content; Node left; Node right; int balance; // -1, 0 oder 1 Node(K k, D c, Node l, Node r, int b){ key=k; content=c; left=l; right=r; balance=b;} außerdem: insertNode, deleteNode, rotateLeft, rotateRight, findMax, rebalanceLeft usw. s. Webseite } // Ende von class Node protected Node root = null; private boolean flag = false; // Hilfsvariable für Einfügen u. Löschen public D find(K key) throws Exception{... analog zu Tree ...} public void insert(K key, D data){... s. Webseite ...} public void delete(K key){... s. Webseite ...} public AVLLWR<K,D> iterator(){return new AVLLWR<K,D>(root);} } 77 Liste Bäume AVL-Baum B-Baum Optimale Bäume Trie kd-Baum Aufwand von Einfügen und Löschen • wie gezeigt, ist die Höhe eines AVL-Baums logarithmisch • Beobachtung: durch Reorganisation sinkt die Höhe an der Rotationsstelle um 1 • ⇒ beim Einfügen ist höchstens eine lokale Reorganisation mit insert (n) ∈ O(log n) konstantem Aufwand nötig ⇒ tW • beim Löschen ist erforderlich: • höchstens ein Aufruf von findMax (mit Aufwand O(log n)) • an jedem (von O(log n)) besuchten Knoten höchstens eine Reorganisation mit konstantem Aufwand: delete (n) ∈ O(log n) ⇒ tW 78 Liste Bäume AVL-Baum B-Baum Optimale Bäume Trie kd-Baum 3.2.2.2 B-Bäume • von Bayer, McCreight 1970 • 2m + 1-ärer Suchbaum • besonders wichtig für Sekundärspeicher (Knoten = ˆ Seite) • Varianten für Hauptspeicher: 2-3-4-Bäume, 2-3-Bäume, Rot-Schwarz-Bäume 79 Liste Bäume AVL-Baum B-Baum Optimale Bäume B-Baum der Ordnung m Trie kd-Baum (m ∈ IN+ ) • jeder Knoten enthält ≤ 2m Schlüssel • jeder Knoten außer der Wurzel enthält ≥ m Schlüssel • jeder innere Knoten mit l Schlüsseln hat l + 1 Nachfolger • alle Blätter haben die gleiche Höhe Beispiel: 30 10 20 24 27 32 34 50 40 45 70 63 66 80 90 80 Liste Bäume AVL-Baum B-Baum Optimale Bäume Trie kd-Baum B-Baum in Java public class Btree<K extends Comparable<K>,D> implements MyCollection<K,D{ protected Bnode<K,D> root = null; protected int ord; public Btree(int n){ord = n;} public D find(K k) throws Exception { if (root == null) throw new Exception(k+" nicht gefunden"); return root.searchNode(k);} public void insert(K k, D c){... s.u....} public void delete(K k) {... s.u....} public BBLWR<K,D> iterator(){return new BBLWR<K,D>(root);} } 81 Liste Bäume AVL-Baum B-Baum Optimale Bäume Trie kd-Baum Klasse Bnode class Bnode<K extends Comparable<K>, D> { int count = 0; int ord; Entry<K,D> entry[]; public Bnode(int n){ord = n; entry = new Entry[2*ord+2];} D searchNode(K k) throws Exception {... s.u....} Entry<K,D> insertNode(K k, D c) {... s.u....} boolean deleteNode(K k){... s.u....} private Entry<K,D> findMin(){ if (entry[0].next != null) return entry[0].next.findMin(); else return entry[1];} private boolean underflow(int i) {... s. Webseite ...} // ggf. Ausgleich bzw. Verschmelzen mit linkem Nachbarn } 82 Liste Bäume AVL-Baum B-Baum Optimale Bäume Trie kd-Baum Klasse Entry class Entry<K extends Comparable<K>, D> { K key; D content; Bnode<K,D> next; Entry(K k, D d, Bnode<K,D> n){ key=k; content=d; next=n;} } 83 Liste Bäume AVL-Baum B-Baum Optimale Bäume Trie kd-Baum B-Baum: Suchen • suche die Position von k count=3 für Überlauf frei im betrachteten Knoten 0 • wenn k gefunden: fertig entry 1 2 3 30 50 70 4 5 • sonst suche im Kindknoten “gemäß der Position” von k weiter 84 Liste Bäume AVL-Baum B-Baum Optimale Bäume Trie kd-Baum B-Baum in Java: Suchen D searchNode(K k) throws Exception { int i=1; // alternativ: binaere Suche while (i<=count && entry[i].key.compareTo(k)<0) i++; if (i<=count && entry[i].key.equals(k)) return entry[i].content; if (entry[--i] != null) { if (entry[i].next == null) throw new Exception(k+" nicht gefunden");} else throw new Exception(k+" nicht gefunden"); return entry[i].next.searchNode(k);} 85 Liste Bäume AVL-Baum B-Baum Optimale Bäume Trie kd-Baum Einfügen in B-Bäumen • Blatt ansteuern wie bei Suche und dort einfügen • überlaufende Knoten teilen • „Trenneintrag“ in Vorgängerknoten einfügen → ggfs. neuer Überlauf • bei Überlauf der Wurzel: neue Wurzel über Teile der alten setzen 86 Liste Bäume AVL-Baum B-Baum Optimale Bäume Trie kd-Baum Beispiel: Einfügen in B-Bäumen 30 10 20 24 27 32 34 50 40 70 45 63 66 80 90 insert(95) insert(42) 30 10 20 24 27 32 40 34 50 42 70 45 63 66 80 90 95 insert(22) 40 22 10 20 30 24 27 50 32 34 42 45 63 70 66 80 90 95 87 Liste Bäume AVL-Baum B-Baum Optimale Bäume Trie kd-Baum Einfügen in B-Baum: Klasse Btree public void insert(K k, D c) { if (root == null) root = new Bnode<K,D>(ord); Entry<K,D> newEntry = root.insertNode(k, c); if (newEntry != null) { Bnode<K,D> oldroot = root; root = new Bnode<K,D>(ord); root.count = 1; root.entry[0] = new Entry<K,D>(null, null, oldroot); root.entry[1] = newEntry;}} 88 Liste Bäume AVL-Baum B-Baum Optimale Bäume Trie kd-Baum Einfügen in B-Baum: Klasse Bnode Entry<K,D> insertNode(K k, D c){ int i = 1; Entry<K,D> newEntry; while (i <= count && entry[i].key.compareTo(k)<0) i++; if (entry[--i] == null) entry[i] = new Entry<K,D>(null, null, null); if (entry[i].next != null) newEntry =entry[i].next.insertNode(k, c); else newEntry = new Entry<K,D>(k, c, null); if (newEntry != null) { //newEntry an Position i+1 einfügen for (int j = ++count; j>i+1; j--) entry[j] = entry[j-1]; entry[i+1] = newEntry; if (count > 2*ord){ // Knoten teilen Bnode<K,D> brother = new Bnode<K,D>(ord); int l = 0; for (int j = count/2+1; j <= count; j++) brother.entry[l++] = entry[j]; count /= 2; brother.count = count; return new Entry<K,D>(entry[count+1].key, entry[count+1].content, brother); else return null;} else return null; } 89 Liste Bäume AVL-Baum B-Baum Optimale Bäume Trie kd-Baum Löschen im B-Bäumen • Blatteintrag direkt löschbar • gelöschten Eintrag in anderem Knoten durch nächstgrößeren Eintrag ersetzen • bei Unterlauf: benachbarte Knoten verschmelzen oder Einträge von Nachbarn herüberholen • Verschmelzen kann Unterlauf des Vorgängers auslösen • bei Unterlauf der Wurzel wird sie gelöscht 90 Liste Bäume AVL-Baum B-Baum Optimale Bäume Trie kd-Baum Ausgleich 60 (m=3) − 1 ... ... 20 30 40 45 50 55 2 3 4 5 6 7 − 75 8 9 80 10 50 − 1 20 30 40 45 − 55 60 75 2 3 4 5 6 7 8 9 80 10 91 Liste Bäume AVL-Baum B-Baum Optimale Bäume Trie kd-Baum Verschmelzen (m = 2) 50 90 - 20 30 - 1 2 3 4 60 6 5 90 - 20 30 50 60 1 2 3 4 5 6 92 Liste Bäume AVL-Baum B-Baum Optimale Bäume Trie kd-Baum Beispiel: Löschen im B-Bäumen 40 22 10 20 30 24 50 27 32 34 42 45 63 70 66 80 90 95 delete (66) 40 22 10 20 30 24 50 27 32 34 42 45 63 80 70 90 95 delete (45) 22 10 20 24 27 30 32 40 80 34 42 50 63 70 90 95 delete (40) 22 10 20 24 27 30 42 32 34 80 50 63 70 90 95 93 Liste Bäume AVL-Baum B-Baum Optimale Bäume Trie kd-Baum Löschen in B-Baum: Klasse Btree public void delete(K k){ if (root != null){ root.deleteNode(k); if (root.count == 0) { if (root.entry[0].next != null && root.entry[0].next.count > 0) root = root.entry[0].next; else if (root.entry[1].next != null && root.entry[1].next.count >0) root = root.entry[1].next; else root = null;}}} 94 Liste Bäume AVL-Baum B-Baum Optimale Bäume Trie kd-Baum Löschen in B-Baum: Klasse Bnode boolean deleteNode(K k){ boolean flag; // underflow? int i = 1; while (i<=count && entry[i].key.compareTo(k)<0) i++; if (i<=count && entry[i].key.equals(k)){ if (entry[i].next == null){ //lösche in Blatt for (int j = i; j<count; j++) entry[j] = entry[j+1]; count--; return (count<ord);} else { //lösche in innerem Knoten Entry<K,D> min = entry[i].next.findMin(); entry[i].key = min.key; entry[i].content = min.content; flag = entry[i].next.deleteNode(min.key);} else if (entry[--i].next == null) return false; else flag = entry[i].next.deleteNode(k); if (flag) return underflow(i); // underflow s. Webseite else return false;} } 95 Liste Bäume AVL-Baum B-Baum Optimale Bäume Trie kd-Baum Aufwand • für # Knoten v gilt: v ≤ dn/me + 1 (1) sowie v ≥ 2 ∗ (m + 1)h−1 daher: h (2) ≤ 1 + logm+1 v 2 ≤ 1 + logm+1 dn/me+1 2 ∈ O(log n) fallsm > 0 6 • z. B. bei n = 2 ∗ 10 und m = 100: h ≤ 3 • da Aufwand für Suchen, Einfügen und Löschen proportional zur Höhe: search insert delete tW (n), tW (n), tW (n) ∈ O(log n) • da jeder Knoten (außer Wurzel) mindestens m von 2m möglichen Einträgen enthält: Speicherplatzausnutzung α ≥ 50% (wichtig bei Sekundärspeicher!) • durch leichte Modifikation von insert und delete: α ≥ 66% 96 Liste Bäume AVL-Baum B-Baum Optimale Bäume Trie kd-Baum 3.2.3 Optimale Suchbäume • manchmal sind die Suchwahrscheinlichkeiten der Schlüssel bekannt • in der Regel ist bei solchen Anwendungen die Menge S = {s1 , . . . , sn } der gespeicherten Schlüssel fest (weder Einfügen noch Löschen) (o.B.d.A. si < sj falls i < j, S ⊆ IN; s0 := −∞, sn+1 := ∞) • Beispiel: Schlüsselworttabelle eines Compilers • Idee: baue Suchbaum so, dass Gesamtsuchzeit minimal pi : Wahrscheinlichkeit, dass si gesucht (i = 1, . . . , n) qi : Wahrscheinlichkeit, dass Schlüssel s mit si < s < si+1 gesucht (i = 0, . . . , n) n X i=1 pi + n X j=0 qj = 1 97 Liste Bäume AVL-Baum B-Baum Optimale Bäume Trie kd-Baum Zugriffswahrscheinlichkeiten und Weglänge • Wahrscheinlichkeiten oft experimentell bestimmt • wenn bei m Versuchen • ai -Mal si gesucht (i = 1, . . . , n) • bj -Mal s ∈ (sj , sj+1 ) gesucht • dann Annahme: pi = ai m, qj = (j = 0, . . . , n), bj m • betrachte Baum T : • sei hi : # Knoten, auf Weg von Wurzel zu si • hi0 : (i = 1, . . . , n) 1+ Anzahl besuchter Knoten bei Suche nach s ∈ (si , si+1 ) (i = 0, . . . , n) • dann: kumulierte gewichtete Weglänge w von T w= n X i=1 • Ziel: minimiere w ai ∗ hi + n X bj ∗ hj0 j=0 98 Liste Bäume AVL-Baum B-Baum Optimale Bäume Trie kd-Baum Bestimmung des optimalen Suchbaums • Ansatz (dynamische Programmierung): ausgehend von optimalen Bäumen für Teile M1 , M2 des Wertebereichs IN bestimme optimalen Baum für M1 ∪ M2 • Notation: Ti,j : optimaler Suchbaum für Wertebereich (si , sj+1 ) und gespeicherte Schlüssel si+1 , . . . , sj ci,j : # Besuche von Ti,j wi,j : kumulierte gewichtete Weglänge von Ti,j 99 Liste Bäume AVL-Baum B-Baum Optimale Bäume Trie kd-Baum Bestimmung des optimalen Suchbaums (2) ci,j = j P k =i+1 ak + j P (0 ≤ i ≤ j ≤ n) bk k =i wi,i = bi wi,j = ci,j + (i = 0, . . . , n) minjk =i+1 (wi,k −1 + wk ,j ) (0 ≤ i < j ≤ n) • der optimale Suchbaum T0,n besteht aus den Teilbäumen Ti,j , deren wi,j “zu w0,n beitragen” 100 Liste Bäume AVL-Baum B-Baum Optimale Bäume Trie kd-Baum Aufwand • es gibt ≤ n2 Werte wi,j • jeder in j − i ≤ n Schritten bestimmbar (min!) ⇒ O(n3 ) Schritte • verbesserbar zu O(n2 ) (→ Knuth) durch verkleinerten Suchbereich bei min-Bildung Bemerkung: • wenn Einfügen, Löschen und sich ändernde Zugriffswahrscheinlichkeiten erlaubt: → selbstorganisierende Bäume (→ Mehlhorn, Bd. III, Kap. 6) d.h. gewichtsbalancierte Bäume, bei denen Gewicht abh. von Zugriffshäufigkeit 101 Liste Bäume AVL-Baum B-Baum Optimale Bäume Trie kd-Baum Beispiel: Optimaler Suchbaum (m = 100) i 1 2 3 si 27 42 68 ai 10 20 30 j 0 1 2 3 (sj , sj+1 ) (−∞, 27) (27, 42) (42, 68) (68, ∞) bj 5 5 10 20 Berechnung von ci,j und wi,j : ci,j : j\i 0 1 2 3 j\i 0 1 2 3 0 5 − − − 0 5 − − − 1 20 5 − − wi,j : 1 30 5 − − 2 50 35 10 − 2 90 50 10 − 3 100 85 60 20 3 210 155 90 20 102 Liste Bäume AVL-Baum B-Baum Optimale Bäume Trie kd-Baum Optimaler Baum im Beispiel T 68 0,3 30 42 − 40 40 27 − 30 30 − − 20 20 insgesamt: w 0,3 zum Vergleich: = 210 42 20 27 68 20 60 − − − 15 15 30 insgesamt: w − 0,3 60 = 220 103 Liste Bäume AVL-Baum B-Baum Optimale Bäume Trie kd-Baum 3.2.4 Alphabetische Bäume (Tries) bei bisherigen Suchbäumen: • Knoten enthalten vollständige Schlüssel • bei langen Schlüsseln großer Platzverbrauch Alphabetische Bäume: • jede Kante mit einem Zeichen des Schlüssels beschriftet • Suche, Einfügen und Löschen folgen Kanten mit gewünschter Beschriftung • gut, falls viele Schlüssel gleiche Präfixe haben Beispiel: a f affe x axt h k a a s u hase haus kahn o korn u kuh 104 Liste Bäume AVL-Baum B-Baum Optimale Bäume Trie kd-Baum 3.2.5 Mehrdimensionale Bäume • wichtig in : (relationalen) Datenbanken, Data Warehouses, Computergrafik, Geo-Informationssytemen, OLAP • Wertebereich D := D1 × · · · × Dk • Schlüsselmenge S = {s1 , . . . , sn } ⊂ D 105 Liste Bäume AVL-Baum B-Baum Optimale Bäume Trie kd-Baum Anfragen • exakte Suche (exact match query) vi ∈ Di search(v1 , . . . , vk ) Beispiel: search(137, ”Koetter ”) • partielle Übereinstimmungssuche (partial match query) vi ∈ Di ∪ {∗} search(v1 , . . . , vk ) Beispiel: search(∗, ”Koetter ”) • Bereichssuche (range query) search(r1 , . . . , rk ) wobei Intervall ri = [vi , vi0 ] ⊂ Di mit vi , vi0 ∈ Di Beispiel: search([0, 9999], [”Kaemper ”, ”Koetter ”]) weitere Operationen: • insert(v1 , . . . , vk ) • delete(. . . ) vi ∈ Di Parameter wie bei Anfragen 106 Liste Bäume AVL-Baum B-Baum Optimale Bäume Trie kd-Baum kd-Baum • binärer Suchbaum • meist inhomogene Variante (Daten in Blättern, in inneren Knoten nur Schlüssel), da Löschen leichter • Knoten v auf Stufe iv (mit iv ≥ 0) teilt Suchraum gemäß dv ∈ D(iv mod k )+1 : • für jeden Schlüssel s = (x1 , . . . , xk ) im Teilbaum Lv links von v ist x(iv mod k )+1 ≤ dv • analog im Teilbaum Rv rechts von v : x(iv mod k )+1 > dv 107 Liste Bäume AVL-Baum B-Baum Optimale Bäume Trie kd-Baum Beispiel: kd-Baum 42 (k = 2) m 27 (27,a) g 32 (35,a) (9,x) (50,g) (50,x) p (35,n) (33,x) induzierte Aufteilung des Suchraums: 60 50 40 30 20 10 a g mn p x 108 Liste Bäume AVL-Baum B-Baum Optimale Bäume Trie kd-Baum Exakte Suche nach (x1 , . . . , xk ) • an innerem Knoten v suche in Lv weiter, wenn xiv mod k +1 ≤ dv sonst suche in Rv weiter • an Blatt vergleiche (x1 , . . . , xk ) mit Eintrag → Aufwand wie beim eindimensionalen Suchbaum tAemq (n) ∈ O(log n) falls h ∈ O(log n) 109 Liste Bäume AVL-Baum B-Baum Optimale Bäume Trie kd-Baum Partielle Übereinstimmungs-Suche nach (x1 , . . . , xk ) • Annahme: t < k Komponenten 6= ∗ • an innerem Knoten v : • suche in Lv weiter, wenn ∗ = 6 xiv mod k +1 • suche in Rv weiter, wenn ∗ = 6 xiv • suche in Lv und Rv , wenn xiv ≤ dv mod k +1 mod k +1 > dv =∗ • an Blatt: vergleiche Eintrag mit Suchmuster Aufwand: tApmq (n) ∈ O((2k −t ) k ) = O((2h ) h k −t k t ) = O(n1− k ) falls h = log2 n und 0 < t < k 110 Liste Bäume AVL-Baum B-Baum Optimale Bäume Trie kd-Baum Bereichsanfrage nach ([u1 , o1 ], . . . , [uk , ok ]) • an innerem Knoten v : • suche in Lv weiter, wenn oiv mod k +1 ≤ dv • suche in Rv weiter, wenn uiv mod k +1 > dv • sonst suche in Lv und Rv weiter uiv mod k +1 ≤ dv < oiv (dann: mod k +1 ) • an Blatt: vergleiche Eintrag mit Suchmuster • Aufwand abhängig von Bereichsgröße (zwischen O(log n) und O(n)) Einfügen: tAinsert (n) ∈ O(log n) falls h = log2 n Löschen: tAdelete (n) ∈ O(log n) falls h = log2 n, Bereich klein Variante: Quad-Tree • jeder innere Knoten teilt Suchraum in 4 Teile (Quadranten) 111