15.06.2010 Bäume O1 O2 O4 O3 O5 O6 O7 Prof. Dr. Margarita Esponda Prof. Dr. Margarita Esponda 1 15.06.2010 Inhalt 1. Einführung 2. Warum Bäume? 3. Listen und Arrays vs. Bäume 4. Einfach verkettete binäre Bäume Baumtraversierung Suchen Einfügen 5. Doppelt verkettete binäre Bäume Löschen Prof. Dr. Margarita Esponda 2 15.06.2010 Warum Bäume? Bäume sind fundamentale Datenstrukturen für: Betriebssysteme Datenbanken Übersetzerbau Textverarbeitung Graphische Verarbeitung Datenkompression Spiele … usw. Prof. Dr. Margarita Esponda 3 15.06.2010 Was ist ein Baum? Eine spezielle Graph-Struktur ohne Zyklen root 1. Er hat eine Wurzel. O1 2. Alle Knoten außer der Wurzel haben genau eine Verbindung O3 O2 mit einem Vorfahren. O5 O4 O11 O7 3. Es existiert genau ein Weg zwischen der Wurzel und O10 O9 O8 O6 jedem beliebigen Knoten. O12 Prof. Dr. Margarita Esponda O13 O14 4 15.06.2010 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 O7 O9 O10 Ein Baum mit N Knoten hat N-1 Kanten. Prof. Dr. Margarita Esponda 5 15.06.2010 Binärbäume • Bäume, in dem 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 Prof. Dr. Margarita Esponda O10 O9 O11 O12 O13 O14 6 15.06.2010 Warum Bäume? Weil die Grundoperationen für dynamische Datenmengen damit viel effizienter realisiert werden können. Suchen Elementare Operationen 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. Prof. Dr. Margarita Esponda 7 15.06.2010 Suchen Sortiertes Array Sortierte Liste mit n Elementen mit n Elementen Kopf Binärsuche 120 7 120 13 7 13 21 21 33 33 41 41 n/2 50 50 66 66 79 n/4 89 103 n/8 Schlimmster Fall log2(n) Schritte 120 200 89 103 Schlimmster Fall n Schritte 120 O(log2n) Prof. Dr. Margarita Esponda 79 200 O(n) 8 15.06.2010 Einfüge- und Lösch-Operationen ( wenn die Position bereits bekannt ist ) Liste Array 2 5 7 11 7 13 11 23 13 41 23 Die Laufzeit hängt linear von der Länge der bereits gespeicherten Daten ab. 2 5 7 56 41 56 13 23 Schlimmster Fall n Schritte O(n) Prof. Dr. Margarita Esponda 11 Die Laufzeit ist immer konstant. 41 Schlimmster Fall 56 O(1) 9 15.06.2010 Liste vs. Array Liste Array 1 2 3 . . . . . . . O1 O2 O3 1 2 3 O1 O2 O3 O4 O5 O6 n O7 . . . . . . . O4 O5 O6 O8 n O7 O8 Elementare Operationen für dynamische Mengen Liste Schlimmster Fall Suchen n Array Schlimmster Fall log2(n) Einfügen konstant + n n + log2(n) Löschen konstant + n n + log2(n) Prof. Dr. Margarita Esponda 10 15.06.2010 Vollständige Binärbäume Ein vollständiger binärer Baum hat 2h – 1 innere Knoten und 2h Blätter n = 2h+1 - 1 root ⇓ Level 0 n + 1 = 2h+1 O1 ⇓ Level 1 O2 O4 O3 O5 O6 h O7 Level 2 log2(n+1) = log2(2h+1) ⇓ log2(n+1) = h+1 O8 O9 O10 O11 O12 O13 O14 O15Level 3 ⇓ h = log2(n+1) -1 Prof. Dr. Margarita Esponda 11 15.06.2010 Binärbäume Binärbäume einfachste Beispiele: Baumstrukturen AVL-Bäume ausgeglichene Bäume Red-Black-Bäume B-Bäume Die wichtigste Voraussetzung für die effiziente Verwaltung von Datenmengen mit Hilfe von Bäumen ist, dass die Bäume balanciert sind. Prof. Dr. Margarita Esponda 12 15.06.2010 Binäre Suchbäume geordneter Baum mit O1 maximal 2 Nachfolgern left right pro Knoten Innere Knoten O2 O3 left right left right O4 null null O5 null null O6 null null O7 null null Blätter Prof. Dr. Margarita Esponda 13 15.06.2010 Wie können wir Binärbäume in Java implementieren? root O3 left right TreeNode-Klasse BinarySearchTree-Klasse O3 null O3 right left right O3 left Die Objekte werden null O3 null null nach einem Schlüssel O3 einsortiert. null Prof. Dr. Margarita Esponda null 14 15.06.2010 Einfach verkettete Binärsuchbäume, um Daten nach einem Schlüsselwert zu speichern. Sortierbare Schlüssel eigentliche Daten public class BinarySearchTree <T extends Comparable<T>, D> implements Iterable<T> { private TreeNode root; private int size; // number of TreeNodes um for-each-Schleifen verwenden zu können public BinarySearchTree() { // constructor root = null; size = 0; } public int size() { return size; } … Prof. Dr. Margarita Esponda 15 15.06.2010 Einfach verkettete Binärsuchbäume, um Daten nach einem Schlüsselwert zu speichern. public class BinarySearchTree …. … private class TreeNode { private T key; private D data; private TreeNode left; private TreeNode right; TreeNode (T key, D data) { this.key = key; this.data = data; size++; } } … Prof. Dr. Margarita Esponda root O3 left right O3 O3 null right left right O3 left null O3 null null O3 null null 16 15.06.2010 Suchen root 4 node 11 left right 7 19 left right left right 3 left right 1 null null Prof. Dr. Margarita Esponda null left right null null 21 left right left right 4 left right 14 9 12 null null left right null null left right null null 17 15.06.2010 Suchen root 4 node 11 left right 7 19 left left right left right 3 leftright right null null null left right null null 21 left right left right 4 1 left right 14 9 12 null null left right null null left right null null Wenn node gleich null wird, befindet sich das Element nicht in dem Baum. Prof. Dr. Margarita Esponda 18 15.06.2010 Einfach verkettete Binärsuchbäume, um Daten nach einem Schlüsselwert zu speichern. … 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; else if (compare > 0) node = node.right; else Gibt die Daten, die mit return node.data; einem Schlüssel verbunden } sind, zurück oder null, return null; wenn der Schlüssel nicht } … vorhanden ist. Prof. Dr. Margarita Esponda 19 15.06.2010 Einfügen (iterativ) root 6 x y 11 xy 19 7 x y 3 1 NULL y x NULL 9 NULL 23 NULL NULL NULL 16 5 NULL 15 NULL NULL NULL x Prof. Dr. Margarita Esponda 20 15.06.2010 Einfach verkettete Binärsuchbäume, um Daten nach einem Schlüsselwert zu speichern. Ein Schlüssel und das damit verbundene Daten-Objekt werden public class BinarySearchTree <T eingegeben extends Comparable<T>, D> implements Iterable<T> { public void store(T key, D data) { root = insert( root, key, data ); } private TreeNode insert(TreeNode node, T key, D data) { if (node == null) return new TreeNode(key, data); Ein neues Objekt int compare = key.compareTo(node.key); wird nach seinem if (compare < 0) Schlüssel in einem node.left = insert(node.left, key, data); Blatt einsortiert. } else if (compare > 0) node.right = insert(node.right, key, data); else node.data = data; Wenn der Schlüssel bereits return node; existiert, werden die Daten … überschrieben. Prof. Dr. Margarita Esponda 21 15.06.2010 Traversierung binärer Bäume Inorder Linker Unterbaum - Wurzel - Rechter Unterbaum F D B A I E J G C H A B C D E F G H I J Prof. Dr. Margarita Esponda 22 15.06.2010 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> { ... } } Prof. Dr. Margarita Esponda 23 15.06.2010 Iterative Implementierung r F rr s t x s Stapel D u I t w B u v A Prof. Dr. Margarita Esponda E C z y J G q H 24 15.06.2010 Iterative Implementierung r F x s Stapel D rr s t I t w B u v A Prof. Dr. Margarita Esponda E C z y J G q H 25 15.06.2010 Iterative Implementierung r F x s Stapel D rr s c I t w B B u v A Prof. Dr. Margarita Esponda E C z y J G q H 26 15.06.2010 Iterative Implementierung r F x s Stapel D rr s I t w B B u v A Prof. Dr. Margarita Esponda E C z y J G q H 27 15.06.2010 Iterative Implementierung r F x s Stapel D rr w I t w B B u v A Prof. Dr. Margarita Esponda E C z y J G q H 28 15.06.2010 Iterative Implementierung r F x s Stapel D rr t w B B u Prof. Dr. Margarita Esponda E E v A I C z y J G q H 29 15.06.2010 Iterative Implementierung r F F x s Stapel D rx y t w B B u Prof. Dr. Margarita Esponda E E v A I C z y J G q H 30 15.06.2010 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(); pushLeftTree(node.right); return node.key; } public void pushLeftTree(TreeNode node) { while (node != null) { stack.push(node); node = node.left; } } public void remove() { throw new UnsupportedOperationException();} } Prof. Dr. Margarita Esponda 31 15.06.2010 Anwendungsbeispiel: public static void main(String[] args) { BinarySearchTree<Integer, String> st = new BinarySearchTree<Integer, String>(); st.store(43901, "Peter Meyer" ); st.store(43021, "Nils Meyer" ); st.store(43002, "Andre Meyer" ); st.store(43101, "Hans Meyer" ); st.store(43000, "Joachim Meyer" ); st.store(43501, "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); } } Prof. Dr. Margarita Esponda 32 15.06.2010 Minimum und Maximum 53 Minimum 27 13 Maximum 69 34 17 Prof. Dr. Margarita Esponda 63 Der erste Knoten, der keine linken Kinder mehr hat, beinhaltet das kleinste Element. 95 46 33 15.06.2010 Nachfolger 1. Fall 53 Es gibt einen rechten Unterbaum. Minimum 69 30 13 8 34 63 17 95 46 39 Prof. Dr. Margarita Esponda 34 15.06.2010 Nachfolger 2. Fall 53 Es gibt keinen rechten Unterbaum. 69 30 13 8 34 63 17 46 95 Maximum 39 Wie können wir nach oben laufen? Prof. Dr. Margarita Esponda 35 15.06.2010 Doppelt verkettete Bäume y 53 parent node 30 13 y O3 69 node y 34 left 63 right 95 node 8 17 46 39 Prof. Dr. Margarita Esponda private class TreeNode { private T key; private D data; private TreeNode left private TreeNode right; private TreeNode parent; … } 36 15.06.2010 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 27 69 69 null 13 34 17 63 95 13 34 95 46 46 garbage Prof. Dr. Margarita Esponda 63 garbage 37 15.06.2010 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 95 13 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. Prof. Dr. Margarita Esponda 38 15.06.2010 Löschen 53 node 27 13 69 34 63 95 left 8 y 30 x Prof. Dr. Margarita Esponda 46 parent 32 39 15.06.2010 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 77 60 62 Prof. Dr. Margarita Esponda 40 15.06.2010 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 95 13 102 34 17 30 69 63 102 200 200 Prof. Dr. Margarita Esponda 41 15.06.2010 Elementare Operationen für dynamische Mengen Liste Schlimmster Fall Array Balancierter Binärbaum Schlimmster Fall 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)) Prof. Dr. Margarita Esponda 42