Bäume O1 O2 O4 O3 Text O5 O6 O7 Prof. Dr. Margarita Esponda SS 2012 22. ALP2-Vorlesung, M. Esponda Inhalt 1. Einführung 2. Warum Bäume? 3. Listen und Arrays vs. Bäume 4. Einfach verkettete binäre Suchbäume Baumtraversierung Suchen Einfügen 5. Doppelt verkettete binäre Bäume Löschen 22. ALP2-Vorlesung, M. Esponda Warum Bäume? Bäume sind fundamentale Datenstrukturen für: Betriebssysteme CFS von Linux (RB-Baum) Datenbanken B-Bäum, R-Bäume Übersetzerbau Abstrakte Syntaxbäume Textverarbeitung 3D Graphik-Systeme Datenkompression Hofmann-Kodierung KI, Spiele Entscheidungsbäume … usw. 22. ALP2-Vorlesung, M. Esponda Was ist ein Baum? Eine spezielle Graph-Struktur ohne Zyklen Wurzel 1. Er hat eine Wurzel. O1 O2 O5 O4 O6 haben genau eine Verbindung O7 mit einem Vorfahren. 3. Es existiert genau ein Weg zwischen der Wurzel und O10 O9 O8 2. Alle Knoten außer der Wurzel O3 jedem beliebigen Knoten. O11 O12 O13 22. ALP2-Vorlesung, M. Esponda O14 Eigenschaften von Bäumen? Nehmen wir an, wir haben einen Baum t, dann gilt: • |t| bezeichnet die Größe des Baumes t oder die gesamte Anzahl seiner Knoten. • Die Tiefe (Level) eines Knotens ist sein Abstand zur Wurzel. Die Tiefe der Wurzel ist gleich 0. • die Höhe h(t) ist der maximale Abstand zwischen der Wurzel und die Knoten. • Blätter sind Knoten ohne Kinder. • Die Pfadlänge des Baumes sei definiert als die Summe der Tiefen aller Knoten des Baumes. 22. ALP2-Vorlesung, M. Esponda Eigenschaften von Bäumen • Zwischen zwei beliebigen Knoten in einem Baum existiert genau ein Pfad, der sie verbindet. O1 Kanten O3 O2 Knoten O4 O8 • O6 O5 O9 Ein Baum mit N Knoten hat N-1 Kanten. 22. ALP2-Vorlesung, M. Esponda O7 O10 Blätter Binärbäume Bäume, in denen jeder Knoten höchstens zwei Kinder hat. Ein Binärbaum mit N inneren Knoten hat N+1 äußere Knoten oder Blätter. O5 Innere Knoten Blätter 22. ALP2-Vorlesung, M. Esponda O10 O9 O11 O12 O13 O14 Warum Bäume? Weil die Grundoperationen für dynamische Datenmengen damit viel effizienter realisiert werden können. Elementare Operationen Suchen für dynamische Mengen Einfügen Löschen Bäume kombinieren die Vorteile der zwei Datenstrukturen, die wir bereits diskutiert haben: Felder (Arrays) und Listen. 22. ALP2-Vorlesung, M. Esponda Suchen Sortiertes Array Sortierte Liste mit n Elementen mit n Elementen Binärsuche 120 Kopf 13 21 21 33 41 41 50 50 66 66 n/4 n/8 79 89 103 120 200 22. ALP2-Vorlesung, M. Esponda 120 13 7 33 n/2 7 Schlimmster Fall log2(n) Schritte O(log2n) 79 89 103 120 Schlimmster Fall n Schritte O(n) Einfüge- und Lösch-Operationen (wenn die Position bereits bekannt ist) Array 2 5 7 11 7 13 11 13 23 Die Laufzeit hängt linear von der Länge der bereits gespeicherten Daten ab. Liste 2 5 7 23 41 56 41 56 22. ALP2-Vorlesung, M. Esponda Schlimmster Fall 11 Die Laufzeit ist immer konstant. 13 23 n Schritte 41 Schlimmster Fall O(n) 56 O(1) Liste vs. Array Liste 1 2 3 O1 O2 O3 1 Array 2 3 O2 O3 O1 . . . . . . . O4 O5 O6 n O7 . . . . . . . O4 O5 O6 O8 n O7 O8 Elementare Operationen für dynamische Mengen Liste sortiert nicht sortiert Array sortiert Suchen O(n) O(n) O(log2(n)) Einfügen O(n) O(1) O(n + log2(n)) Löschen O(n) O(n) O(n + log2(n)) 22. ALP2-Vorlesung, M. Esponda Vollständige Binärbäume Ein vollständiger binärer Baum hat 2h – 1 innere Knoten und 2h Blätter root n = 2h+1 - 1 O1 O2 O4 O8 O10 O6 O11 O12 22. ALP2-Vorlesung, M. Esponda ⇓ h Level 2 O7 O13 n + 1 = 2h+1 Level 1 O3 O5 O9 ⇓ Level 0 O14 O15 Level 3 log2(n+1) = log2(2h+1) ⇓ log2(n+1) = h+1 ⇓ h = ⎡log2(n+1)⎤ -1 Eigenschaften von Binär Bäumen Rekursive Definitionen: Anzahl der inneren Knoten t = tl + t r + 1 Höhe des Baumes h(t) = 1 + max(h(tl ) + h(t r )) Innere Pfadlänge des Baumes π (t) = π (tl ) + π (t r ) + t − 1 (Summe der Tiefen aller inneren Knoten) Pfadlänge der Blättern (Summe der Pfadlänge der Blättern) 22. ALP2-Vorlesung, M. Esponda ξ (t) = ξ (tl ) + ξ (t r ) + t + 1 Binärbäume Binärbäume einfachste Beispiele: AVL-Bäume Baumstrukturen Red-Black-Bäume ausgeglichene Bäume B-Bäume usw. Die wichtigste Voraussetzung für die effiziente Verwaltung von Datenmengen mit Hilfe von Bäumen ist, dass die Bäume balanciert sind. 22. ALP2-Vorlesung, M. Esponda Binäre Suchbäume geordneter Baum mit maximal O1 2 Nachfolgern pro Knoten left right O2 Innere Knoten left right O4 null left O5 null null Blätter 22. ALP2-Vorlesung, M. Esponda O3 null right O6 null null O7 null null Wie können wir Binärbäume in Java implementieren? root O3 left right TreeNode-Klasse BinarySearchTree-Klasse O3 null O3 left right right O3 Die Objekte werden left null nach einem Schlüssel einsortiert. O3 null 22. ALP2-Vorlesung, M. Esponda null O3 null null root Beispiel: D1 K1 left right TreeNode-Klasse BinarySearchTree-Klasse K2 null D2 Schlüssel verbunden ist. K6 null 22. ALP2-Vorlesung, M. Esponda D4 K5 left null null K4 einem Schlüssel K einsortiert D3 left right right Die Objekte werden nach Datenobjekt D, das zum K3 D6 null D5 null Binäre Suchbäume. Sortierbare Schlüssel Daten public class BinarySearchTree <T extends Comparable<T>, D> implements Iterable<T> { private TreeNode root; private int size; // Anzahl der TreeNode-Objekten public BinarySearchTree() { // constructor root = null; size = 0; } public int size() { return size; } … 22. ALP2-Vorlesung, M. Esponda um for-each-Schleifen verwenden zu können Binäre Suchbäume root public class BinarySearchTree …. … D1 T1 class TreeNode { left right private T key; private D data; private TreeNode left; private TreeNode right; T2 null D2 T3 left right right TreeNode (T key, D data) { D4 T5 left null null T4 this.key = key; this.data = data; D3 D5 null size++; } } // end of class TreeNode T6 null D6 null … 22. ALP2-Vorlesung, M. Esponda 5 Suchen root 4 node 11 left right 7 19 left right left right 3 left right 1 null null null left right null 22. ALP2-Vorlesung, M. Esponda null null 21 left right left right 4 left right 14 9 12 null left right null null left right null null Suchen root Wenn node gleich null wird, befindet sich das Element nicht in node dem Baum. 4 11 left right left 7 19 left left right left right 3 leftright right right 1 null null null left right null 22. ALP2-Vorlesung, M. Esponda null null 21 left right left right 4 left right 14 9 12 null left right null null left right null null Binäre Suchbäume … public boolean contains(T key) { return getData( key ) != null; } Ein Schlüssel wird gesucht public D getData(T key) { TreeNode node = root; while (node != null) { int compare = key.compareTo(node.key); if (compare < 0) node = node.left; Gibt die Daten, die mit else if (compare > 0) node = node.right; einem Schlüssel verbunden else sind, zurück oder null, wenn return node.data; der Schlüssel nicht } return null; vorhanden ist. } … 22. ALP2-Vorlesung, M. Esponda Einfügen (rekursiv) root insert(root, 4, data) 11 7 3 19 D2 D4 9 NULL 1 NULL D8 NULL 5 NULL D1 D9 NULL D5 NULL 15 D3 D6 16 NULL 23 NULL D0 NULL D7 NULL Einfügen (iterativ) root 11 D1 insert(left, 4, data) 7 3 19 D2 D4 9 NULL 1 NULL D8 NULL 5 NULL D9 NULL D5 NULL 15 D3 D6 16 NULL 23 NULL D0 NULL D7 NULL Einfügen (iterativ) root 11 7 D1 19 D2 D3 insert(left, 4, data) 3 D4 9 NULL 1 NULL D8 NULL 5 NULL D9 NULL D5 NULL 15 D6 16 NULL 23 NULL D0 NULL D7 NULL Einfügen (iterativ) root 11 7 3 D1 19 D2 D4 9 D5 15 D3 D6 23 D7 insert(right, 4, data) NULL 1 NULL D8 NULL 5 NULL D9 NULL NULL 16 NULL NULL D0 NULL NULL Einfügen (iterativ) root 11 7 3 19 D2 D4 9 NULL D8 insert(left, 14, data) NULL 5 NULL NULL return 4 D1 D9 NULL data D5 NULL 15 D3 D6 16 NULL 23 NULL D0 NULL D7 NULL Einfügen (iterativ) root 11 7 3 19 D2 D4 9 NULL 1 NULL 5 D8 NULL NULL 4 D1 D9 NULL data NULL D5 NULL 15 D3 D6 16 NULL 23 NULL D0 NULL D7 NULL Binäre Suchbäume public class BinarySearchTree <T extends Comparable<T>, D> implements Iterable<T>{ … Ein Schlüssel und das damit verbundene public void store(T key, D data) { Daten-Objekt werden eingegeben root = insert( root, key, data ); } private TreeNode insert(TreeNode node, T key, D data) { if (node == null) Ein neues Objekt wird return new TreeNode(key, data); nach seinem int compare = key.compareTo(node.key); Schlüssel in einem Blatt einsortiert. if (compare < 0) } … node.left = insert(node.left, key, data); else if (compare > 0) node.right = insert(node.right, key, data); else node.data = data; Wenn der Schlüssel bereits existiert, werden die Daten return node; überschrieben. Traversierung binärer Bäume Inorder Linker Unterbaum - Wurzel - Rechter Unterbaum F D B A I E J G C A B C D E F G H I J H Implementierung einer Iterator-Klasse als Innere Klasse public class BinarySearchTree <T extends Comparable<T>, D> implements Iterable<T> { ... public Iterator<T> iterator() { return new InorderIterator(); } Iterator-Klasse, die den Baum in sortierter Reihenfolge durchläuft. private class InorderIterator implements Iterator<T> { ... } } 22. ALP2-Vorlesung, M. Esponda Iterative Implementierung r F s Stapel x D rr t w B u E v A I C z y G J q H Iterative Implementierung r F s Stapel rr s t x D u t w B u E v A I C z y G J q H Iterative Implementierung r F s Stapel rr s t x D u t w B u E v A I C z y G J q H Iterative Implementierung r F s Stapel x D rr s t t w B u E v A I C z y G J q H Iterative Implementierung r F s Stapel x D rr s v t w B u E v A I C z y G J q H Iterative Implementierung r F s Stapel x D rr s t w B u E v A I C z y G J q H Iterative Implementierung r F s Stapel x D rr w t w B u E v A I C z y G J q H Iterative Implementierung r F s Stapel x D rr t w B u E v A I C z y G J q H Iterative Implementierung r F s Stapel x D rx y t w B u E v A I C z y G J q H Implementierung einer Iterator-Klasse als Innere Klasse ... private class InorderIterator implements Iterator<T> { private Stack<TreeNode> stack = new Stack<TreeNode>(); InorderIterator() { pushLeftTree(root); } public boolean hasNext() { return !stack.isEmpty(); } public T next() { if (!hasNext()) throw new NoSuchElementException(); TreeNode node = stack.pop(); public class NoSuchElementException pushLeftTree(node.right); extends RuntimeException return node.key; } public void pushLeftTree(TreeNode node) { while (node != null) { stack.push(node); node = node.left; } } public void remove() { throw new UnsupportedOperationException();} } ... Anwendungsbeispiel: public static void main(String[] args) { BinarySearchTree<Integer, String> st = new BinarySearchTree<Integer, String>(); st.store(43901, st.store(43021, st.store(43002, st.store(43101, st.store(43000, st.store(43501, "Peter Meyer" ); "Nils Meyer" ); "Andre Meyer" ); "Hans Meyer" ); "Joachim Meyer" ); "Carl Meyer" ); for(Iterator<Integer> iter = st.iterator(); iter.hasNext();) System.out.println(iter.next()); System.out.println("size = " + st.size()); for (Integer s : st) { System.out.println(s); } } Minimum und Maximum 53 Minimum 27 13 Maximum 69 34 17 22. ALP2-Vorlesung, M. Esponda 63 46 95 Der erste Knoten, der keine linken Kinder mehr hat, beinhaltet das kleinste Element. Nachfolger 1. Fall 53 Es gibt einen rechten Unterbaum. Minimum 13 8 69 30 34 63 17 46 39 95 Nachfolger 2. Fall Nachfolger 53 Es gibt keinen rechten Unterbaum. 13 8 69 30 34 63 17 46 95 Maximum 39 Wie können wir nach oben laufen? Doppelt verkettete Bäume 53 parent O3 69 30 left 13 34 63 y 8 17 46 39 right 95 node private class TreeNode { private T key; private D data; private TreeNode left private TreeNode right; private TreeNode parent; … } Doppelt verkettete Bäume 53 parent O3 69 30 y 13 left 34 63 right 95 node 8 17 46 39 private class TreeNode { private T key; private D data; private TreeNode left private TreeNode right; private TreeNode parent; … } Doppelt verkettete Bäume 53 parent O3 69 30 y node 13 8 34 left 63 17 46 39 right 95 private class TreeNode { private T key; private D data; private TreeNode left private TreeNode right; private TreeNode parent; … } Doppelt verkettete Bäume 53 y parent O3 69 30 left node 13 8 34 63 17 46 39 right 95 private class TreeNode { private T key; private D data; private TreeNode left private TreeNode right; private TreeNode parent; … } Doppelt verkettete Bäume node 53 y parent O3 69 30 left 13 8 34 63 17 46 39 right 95 private class TreeNode { private T key; private D data; private TreeNode left private TreeNode right; private TreeNode parent; … } Doppelt verkettete Bäume y 53 parent node O3 69 30 left 13 8 34 63 17 46 39 right 95 private class TreeNode { private T key; private D data; private TreeNode left private TreeNode right; private TreeNode parent; … } Doppelt verkettete Bäume y 53 parent node O3 69 30 left 13 8 34 63 17 46 39 right 95 private class TreeNode { private T key; private D data; private TreeNode left private TreeNode right; private TreeNode parent; … } Doppelt verkettete Bäume y Nachfolger 53 parent node O3 69 30 left 13 8 34 63 17 46 39 right 95 private class TreeNode { private T key; private D data; private TreeNode left private TreeNode right; private TreeNode parent; … } Delete-Operation ( Löschen ) 1. Fall 2. Fall Löschen eines Knotens ohne Kinder Löschen eines Knotens mit nur einem Kind 53 53 27 13 69 34 17 63 27 null 95 13 69 34 63 46 46 garbage garbage 95 Löschen 3. Fall Löschen eines Knotens mit zwei Kindern Der Knoten, den man löschen möchte, wird durch seinen Nachfolger ersetzt. 53 53 27 13 8 17 69 34 30 63 46 69 30 13 95 8 17 34 32 63 95 46 32 Der Nachfolger von 27 ist das Minimum des rechten Unterbaumes. Das Minimum ist entweder ein Blatt oder hat maximal ein rechtes Kind. Löschen 53 node 27 13 69 34 63 left 8 y 30 x 46 parent 32 95 Probleme mit einfachen binären Suchbäumen balancierter Binärbaum nicht balancierter Binärbaum 53 53 27 13 69 34 17 30 63 46 83 30 95 95 59 139 71 65 60 62 77 Lösungen AVL-Bäume Red-Black-Bäume AA-Bäume B-Bäume usw. Innerhalb der insert- und delete-Operationen wird mit Hilfe von Rotationen die Balance des Baumes ständig wiederhergestellt. 53 27 13 69 34 17 53 x 30 63 95 27 13 95 102 34 17 200 30 69 63 102 200 Elementare Operationen für dynamische Mengen Liste Schlimmster Fall Array Schlimmster Fall Balancierter Binärbaum Schlimmster Fall Suchen O(n) O(log2(n)) O(log2(n)) Einfügen O(n) O(n) O(log2(n)) Löschen O(n) O(n) O(log2(n)) 22. ALP2-Vorlesung, M. Esponda Farben R: 0 G: 51 B: 102 R: 153 G: 204 B: 0 R: 255 G: 255 B: 255 R: 0 G: 0 B: 0 R: 204 G: 0 B: 0 R: 255 G: 153 B: 51 22. ALP2-Vorlesung, Fußzeile anpassen unter M. Esponda Ansicht -> Kopf- und Fußzeile 5 2