11. Rekursive Datenstrukturen • Lineare Listen – Liste = leer | Element Liste 1. Die leere Liste ist eine Liste 2. Wird einer Liste ein Element angehängt, so erhalten wir wiederum eine Liste • Rekursive Implementierung von Listen – Die Klasse Node enthält „sich selbst“: Rekursion – Die Liste wird (über node.next) so lange „reduziert“, bis wir zur trivialen (leeren) Liste kommen – Die Suche in einer Schleife wird durch den rekursiven Abstieg ersetzt László Böszörményi ESOP Rekursive Datenstrukturen - 1 Klasse Node und Interface List – unverändert public class Node { // Klasse Node, represäntiert Knoten der Liste Comparable info; // info enthält die Funktion, wonach sortiert wird Node next; // next zeigt auf den nächsten Knoten in der Liste Node (Comparable info, Node next) { this.info = info; this.next = next;} public String toString () { return "" + info; } // Retourniert info als String } // Node public interface List { // 03.11.2000. LB public Comparable search (Comparable key); // Retourniert ein Element mit Schlüssel=key bzw. null, wenn nicht gefunden public void insert (int key); // Fügt einen Knoten mit Schlüssel=key ein public void remove (Comparable key); // Entfernt Knoten mit Schlüssel=key, wenn vorhanden public String toString (); // Wandelt die Liste in eine Zeichenkette um } // List László Böszörményi ESOP Rekursive Datenstrukturen - 2 Benutzerklasse mit Zahlen static void useList(List list) { for (int i = 9; i > 0; i -= 2) list.insert( new CompInt(i) ) ; Out.println(list); for (int i = -2; i < 14; i+= 2) list.insert( new CompInt(i) ) ; Out.println(list); for (int i = -5; i < 16; i+= 4) { CompInt k = (CompInt) list.search (new CompInt(i)); // Suche if ( k != null ) { // gefunden (13579) list.remove ( k ) ; ( -2 0 1 2 3 4 5 6 7 8 9 10 12 ) Out.println(k + " entfernt "); -5 nicht enthalten } else // nicht gefunden -1 nicht enthalten 3 entfernt Out.println(i + " nicht enthalten"); 7 entfernt } // for i 11 nicht enthalten Out.println(list); 15 nicht enthalten } // useList ( -2 0 1 2 4 5 6 8 9 10 12 ) Aufruf: useList (new RekList()); László Böszörményi ESOP Rekursive Datenstrukturen - 3 Rekursive Implementierung von Listen (1) public class RekList implements List { // Klasse List, für sortierte Liste Node head = null; // Kopf der Liste – auf null initialisiert // durch den default-Konstruktor: RekList() Node search (Comparable key, Node node) { // Suche key rekursiv ab node if (node == null) return null; // Nicht gefunden else if (key.compareTo (node.info) > 0) return search(key, node.next); // Rekursiv weitersuchen solange key > aktueller Schlüssel else if (key.compareTo (node.info) == 0) return node; // Gefunden else return null; // Nicht gefunden } // search public Comparable search (Comparable key) { Node n = search(key, head); // Führt die Suche durch if (n == null) return null; else return n.info; // Retourniert das Ergebnis } // search László Böszörményi ESOP Rekursive Datenstrukturen - 4 Rekursive Implementierung von Listen (2) String toString (Node node, String listString) { // Baut String rekursiv auf if (node == null) return listString; // Vor dem Aufruf konkatenieren: else return toString (node.next, listString + node + " "); } // toString // (sonst wäre der String umgekehrt) public String toString () { return "( " + toString (head, "") + ")"; } // toString Node insert(Comparable key, Node node) {// Fügt ab node ein, sucht rekursiv if ( (node == null) || (key.compareTo(node.info) <= 0) ) return new Node(key, node); // Am Kopf der aktuellen Liste else { // Sonst: rekursiv weitersuchen node.next = insert(key, node.next); return node; } // if } // insert public void insert(Comparable key) {// Fügt Knoten mit Schlüssel=key ein head = insert(key, head); } // insert László Böszörményi ESOP Rekursive Datenstrukturen - 5 Rekursive Implementierung von Listen (3) Node remove (Comparable key, Node node) { // Sucht ab node rekursiv if (node == null) return null; // Nicht gefunden else if (key.compareTo (node.info) > 0) { // Rekursiv weitersuchen node.next = remove (key, node.next); // Entfernen return node; // Kette muss aufrechterhalten werden } // if else if (key.compareTo (node.info) == 0) // Gefunden return node.next; // Zieladresse zurück else return node; // Nicht gefunden } // remove public void remove (Comparable key) { head = remove (key, head); } // remove } // RekList László Böszörményi ESOP Rekursive Datenstrukturen - 6 Benutzerklasse mit Personen static void useList(List list) { String [] persons1 = {"Pete", "May", "Paul", "Bob"}; String [] persons2 = {"Sue", "Mic", "Xe", "Xa", "Ada", "Xu"}; for (int i = persons1.length - 1; i >= 0; i -- ) list.insert ( new Person (persons1[i]) ); Out.println(list); for (int i = 0; i < persons2.length; i++ ) list.insert( new Person (persons2[i]) ); Out.println(list); for (int i = 1; i < persons2.length; i+= 2 ) { Person p = new Person (persons2[i]); String s; if ( list.search(p) != null ) { list.remove (p); s = " entfernt"; } else s = " nicht gefunden"; ( Bob May Paul Pete ) Out.println(p + s); ( Ada Bob May Mic Paul Pete Sue Xa Xe Xu ) Mic entfernt } // for i Xa entfernt Out.println(list); Xu entfernt } // useList ( Ada Bob May Paul Pete Sue Xe ) László Böszörményi ESOP Rekursive Datenstrukturen - 7 Lineares Suchen in einem Array • Wir müssen das Array durchsuchen, solange wir entweder – – • Das gesuchte Element gefunden haben oder Das Ende des Arrays erreicht haben Implementierung (iterativ bzw. rekursiv) – Suchfunktion retourniert den Index des gefundenen Elementen bzw. -1 static int LinearSearch (int [] arr, int key) { // Iteratives lineares Suchen int i = 0; while ( (i < arr.length) && (arr[i] != key) ) i++; if (i == arr.length) return -1; // nicht gefunden: gibt -1 zurück else return i; // gefunden an Stelle i } // LinearSearch static int RekSearch (int [] arr, int key, int start) {// Rekursives lineares Suchen if (start == arr.length) return -1; // nicht gefunden : -1 zurück else if (arr[start] == key) return start; // gefunden an Stelle start else return RekSearch(arr, key, start + 1); // weitersuchen ab start+1 } // RekSearch László Böszörményi ESOP Rekursive Datenstrukturen - 8 Bäume („Schnupperkurs“ – Rest in SW2) • • Ein Baum ist ein gerichteter Graph, in dem jeder Knoten außer der Wurzel genau einen Vorgänger („Vater“) hat Eine Baumstruktur mit Grundtyp T ist entweder 1. Die leere Struktur oder 2. Ein Knoten vom Typ T mit einer endlichen Anzahl verknüpfter Baumstrukturen mit Grundtyp T (die sog. Teilbäume) Wurzel Höhe = 3 • • • Bäume sind immer azyklisch Pfadlänge: Die Anzahl der Kanten zwischen zwei Knoten Höhe: Maximale Pfadlänge aus der Wurzel heraus – – Höhe des leeren Baumes = 0 Höhe eines Baumes der nur aus Wurzel besteht = 1 László Böszörményi ESOP Rekursive Datenstrukturen - 9 Binärbäume • • • In einem Binärbaum hat jeder Knoten höchstens 2 Nachfolger („Kinder“) Binärbaum = leer | Knoten Binärbaum Binärbaum Ein Binärbaum ist entweder leer oder besteht aus einem Knoten (Wurzelknoten) und 2 Teilbäume Wurzel Linker „Sohn“ László Böszörményi Rechter „Sohn“ ESOP Höhe = 5 Rekursive Datenstrukturen - 10 Binäre Suchbäume • In einem binären Suchbaum (geordneten Binärbaum) gilt für jeden Knoten, dass – – Alle Schlüsselwerte im linken Teilbaum kleiner Alle Schlüsselwerte im rechten Teilbaum größer oder gleich sind, wie der Schlüsselwert im Knoten selbst (Ob wir gleiche Knoten rechts oder links einordnen ist Definitionssache) 50 20 10 5 70 35 10 László Böszörményi 60 40 90 85 ESOP 95 Rekursive Datenstrukturen - 11 Impliziter Suchbaum – Binäres Suchen • Wir suchen in einem geordneten Array – – – Wir schauen rekursiv, ob ein Element in der linken oder rechten Hälfte des Arrays enthalten ist Ist die Größe des Arrays n, kostet das höchstens log2N Schritte (N ist die kleinste Zweierpotenz > n) Es wird implizit ein Suchbaum, der Höhe log2N aufgebaut 30, 71, 92, 233, 284, 315, 326, 357, 478, 559, 6110, 7211 Suche von 28 315 92 30 478 233 71 László Böszörményi 6110 326 284 357 ESOP Höhe = 4 (log216) 559 7211 Rekursive Datenstrukturen - 12 Implementierung vom Binären Suchen static int BinSearch (int [] arr, int left, int right, int key) { /* Gibt den Index des gefundenen Elementen bzw. -1 (falls nicht gefunden) zurück */ int middle = left + (right - left) / 2; // Wurzel des nächsten Teilbaums Out.println("arr[" + middle + "] = " + arr[middle]); // Testausgabe if (key == arr[middle]) return middle; // key == arr[middle]: gefunden else if (key < arr[middle]) // key < arr[middle]: suche links if (left < middle) return BinSearch(arr, left, middle - 1, key); arr[5] = 31 31 gefunden else return -1; // nicht gefunden (in 1 Schritt) else // key >= arr[middle]: suche rechts if (middle < right) return BinSearch(arr, middle + 1, right, key); else return -1; // nicht gefunden arr[5] = 31 arr[5] = 31 } // BinSearch int [] q = {3, 7, 9, 23, 28, 31, 32, 35, 47, 55, 61, 72}; int x = BinSearch (q, 0, q.length - 1, 28); László Böszörményi ESOP arr[2] = 9 arr[3] = 23 arr[4] = 28 28 gefunden arr[8] = 47 arr[10] = 61 arr[11] = 72 99 nicht gefunden Rekursive Datenstrukturen - 13