Kapitel 12: Induktive Datenstrukturen Felix Freiling Lehrstuhl für Praktische Informatik 1 Universität Mannheim Vorlesung Praktische Informatik I im Herbstsemester 2009 Folien nach einer Vorlage von H.-Peter Gumm, Philipps-Universität Marburg
Überblick
Induktive Datenbereiche Listen, verkettete Listen Doppelt verkettete Listen LinkedList Bäume, Binärbäume
Praktische Informatik I, HWS 2009, Kapitel 12
Seite 2
Induktiv definierte Daten
Die natürlichen Zahlen sind induktiv definiert
0 ist eine natürliche Zahl Wenn N eine natürliche Zahl ist, dann auch N+1
Binärzahlen kann man induktiv definieren
Der leere String “ “ ist ein String Ist S ein String und z ein Zeichen, dann ist S z ein String
““, “a“, “b“, ..., “aa“, “ab“, ... , “ba“, “bb“, ... “aaa“, “aab“, ...
Listen
B0 und B1.
Strings kann man induktiv definieren
0, 1, 00, 01, 10, 11, 000, 001, 010, 011,
Jede der Ziffern 0 und 1 ist eine Binärzahl Ist B eine Binärzahl, dann auch
0, |, ||, |||, ||||, |||||, ||||||, ...
die leere Liste [ ] ist eine Liste ist e ein Element und L = [ x1, …, xn ] eine Liste, dann ist auch cons(e,L) := [ e, x1, …, xn ] eine Liste.
[ ], [ 2,3,5 ], [1,6, 19, 24, 0, 42 ]
Binärbäume (ohne Blätter)
Der leere Baum ist ein Binärbaum Sind B1 und B2 Binärbäume, dann auch
Praktische Informatik I, HWS 2009, Kapitel 12
B1
B2 Seite 3
Listen, rekursiv definiert
Eine Liste ist entweder die leere Liste oder sie hat
public class Liste { // Objektfelder: private E inhalt; private Liste rest;
ein erstes Element und eine Rest-Liste
Objektreferenz null bedeutet leere Liste new Liste(e) ergibt Referenz auf einelementige Liste des Typs E
Praktische Informatik I, HWS 2009, Kapitel 12
Liste definiert mittels Liste
// Konstruktoren: public Liste(E e) { inhalt = e; rest = null; } public Liste(E e, Liste l) { inhalt = e; rest = l; } ... }
Seite 4
... und Test ...
„verkettete Liste“
Praktische Informatik I, HWS 2009, Kapitel 12
Seite 5
Länge berechnen, typisch rekursiv
Länge einer Liste: 1
falls Liste nur ein Element enthält sonst: Länge der Restliste +1
public class ... public int if (rest return } else { return } } ... }
Liste { laenge() { == null) { 1; 1+rest.laenge();
Wie programmiert man das mit einer Schleife?
Praktische Informatik I, HWS 2009, Kapitel 12
Seite 6
hinten Anhängen (append)
Hänge e hinten an die Liste an Typisch rekursiv:
Falls Liste nur ein Element hat, dann hänge e an dieses Element an
Ansonsten, hänge e ans Ende der Restliste an
Einfach erweiterbar auf hintenAnhaengen(Liste x)
Wie programmiert man das mit einer Schleife?
Praktische Informatik I, HWS 2009, Kapitel 12
public void hintenAnhaengen(E e) { if (rest == null) { rest = new Liste(e); } else { rest.hintenAnhaengen(e); } }
Seite 7
Suchen in einer Liste public boolean enthalten(E e) { if (inhalt.equals(e)) { return true; } else { if (rest == null) { return false; } else { return rest.enthalten(e); } } }
Prüft, ob ein Element e in der Liste enthalten ist
Wieder typisch rekursiv
Praktische Informatik I, HWS 2009, Kapitel 12
Warum nicht inhalt == e ?
Seite 8
Kopieren rekursiv: deep copy
gegeben Listen a, b
a = b;
kopiert Referenzen
a = b.kopie();
Soll eine komplette Kopie der Liste b erstellen
Natürliche rekursive Formulierung
Problem: Was ist, wenn inhalt auch eine Referenz ist?
public Liste kopie() { if (rest == null) { return new Liste(inhalt); } else { return new Liste(inhalt, rest.kopie()); } }
inhalt.kopie() aufrufen?! muss definiert sein, mehr dazu später
Rekursives Kopieren alle Felder entlang der Referenzstrukturen nennt man deep copy
Praktische Informatik I, HWS 2009, Kapitel 12
Seite 9
Leere Liste?
Bisher leere Liste mittels einer Nullreferenz dargestellt
Problem: Leere Liste und nichtleere Liste müssen unterschiedlich behandelt werden
Liste a = null;
Was ergibt a.laenge() ... ?
Lösung: Verwende Listenkopf
Liste ist jetzt immer ein Objekt (auch eine leere Liste) Bessere Lösung: Verwende abstrakte Klassen (siehe später)
Praktische Informatik I, HWS 2009, Kapitel 12
Seite 10
Entfernen aus einer Liste?
Gegeben eine Liste a, darin ein Element e Wie
entferne ich e aus a?
Um ein Element zu entfernen, benötigt man eine Referenz auf das davor liegende Listenelement
Praktische Informatik I, HWS 2009, Kapitel 12
Seite 11
Doppelt verkettete Liste
Idee: Speichere nicht nur Referenz auf die Restliste (Suffix) sondern auf den Präfix der Liste
public class Liste { // Objektfelder: private Liste praefix private E inhalt; private Liste suffix; // Konstruktor: public Liste(E e) { inhalt = e; praefix = null; suffix = null; } } public void entfernen(E e) { if (inhalt.equals(e)) { praefix.suffix = suffix; return; } if (suffix != null) { suffix.entfernen(e); } }
Praktische Informatik I, HWS 2009, Kapitel 12
Seite 12
java.util.LinkedList
Universelle Klasse für Listen
Basiert intern auf doppelt verketteter Liste mit Kopf (header)
Praktische Informatik I, HWS 2009, Kapitel 12
Seite 13
Vorteile induktiver Datenstrukturen
Induktive (selbstbezügliche) Datenstrukturen haben keine (wirkliche) Größenbeschränkung
Im Gegensatz zu Arrays kann eine Liste beliebig groß machen Und zwar ohne Umkopieren
Induktive Datenstrukturen ermöglichen elegante, einfache, rekursive Methoden
Nachteil: Solche Datenstrukturen sind „langsamer“ als Arrays
Man kann z.B. nicht direkt an einen Index springen
In Java-Bibliothek gibt es verschiedene Implementierungen von Listen:
java.util.ArrayList : Listen auf Basis von Arrays java.util.LinkedList : Listen auf Basis von doppelt verketteten Listenelementen
Klassen bieten die gleiche Schnittstelle an, sind aber anders implementiert Implementierung hat Auswirkung auf die Effizienz der Programme (siehe Kapitel 16)
Praktische Informatik I, HWS 2009, Kapitel 12
Seite 14
Baum (mathematisch)
Ein Baum B = (V, E) ist ein Tupel bestehend aus
einer Menge V = { v1, v2, ..., vn } von Knoten und einer Menge E ⊆ V × V von (gerichteten) Kanten
Definition aus Kapitel 6
mit folgenden Eigenschaften:
Induktive Definition:
Genau ein Knoten hat keine eingehende Kante (Wurzel). Alle Knoten ausser der Wurzel haben genau eine eingehende Kante. Es gibt keine Zyklen (Rundwege aus Kanten).
Ein “leerer Baum” ist ein Baum Gegeben Bäume B1, ..., Bd, dann ist ein neuer Knoten mit ausgehenden Kanten nach B1 und Bd ebenfalls ein Baum
Wir betrachten zunächst Bäume, die in ihren Knoten Werte speichern (analog zu Listen) und die maximal zwei „Unterbäume“ haben
Praktische Informatik I, HWS 2009, Kapitel 12
Seite 15
Experiment Wie eine Liste, bei der jedes Listenelement zwei Suffixe hat
Praktische Informatik I, HWS 2009, Kapitel 12
Seite 16
Baum-Terminologie
Die Knoten enthalten Datenelemente (bei uns: ganze Zahlen)
Ein Pfad ist eine Liste von Knoten im Baum, in der aufeinander folgende Knoten durch Kanten verbunden sind
Die Länge eines Pfades ist die Anzahl der Knoten im Pfad
Die Pfadlänge eines Baumes ist die Summe aller Pfadlängen von einem Knoten zur Wurzel.
Jeder Knoten (außer der Wurzel) hat genau einen Elternknoten
Die durch ausgehende Kanten mit ihm verbundenen Knoten sind seine Kinder
Ein Knoten, der keine Kinder hat, heißt Blattknoten
Jeder Knoten ist die Wurzel eines Unterbaumes
Die Tiefe eines Baumes ist die Länge des längsten Pfades von der Wurzel zu einem Blatt
Ein Baum heißt vom Rang d, wenn jeder Knoten maximal d Kinder hat
Ein Baum heißt Binärbaum genau dann, wenn er vom Rang 2 ist, d.h. wenn jeder Elternknoten maximal zwei Kinder hat.
Praktische Informatik I, HWS 2009, Kapitel 12
Beobachtung: Zwischen der Wurzel des Baumes und jedem anderen Knoten gibt es genau einen Pfad.
Seite 17
Doppelte Rekursion: Tiefe
Berechnung der Tiefe eines Binärbaums Tiefe
= Länge des längsten „Asts“ von der Wurzel aus
Warum geht das so nicht?
Praktische Informatik I, HWS 2009, Kapitel 12
Seite 18
Programmtechnische Darstellung von Bäumen
Bisher implementiert: Binärbaum
Wie implementiert man allgemeine Bäume von Rang d?
Problem: Jeder Knoten kann eine andere Anzahl von Kindern haben, maximal d.
Lösung 1: Parent-Link-Darstellung
Lösung 2: Explizite Speicherung der Kinder
In jedem Knoten wird nur der Zeiger auf seinen Elternknoten gespeichert. Ist in der Regel nur sinnvoll, wenn der Baum nur von unten nach oben durchlaufen werden soll
In einer verketteten Liste oder einem Array Falls ein Array benutzt wird, muss maximaler Rang des Baumes bei der Implementierung bekannt sein
Lösung 3: Listendarstellung der Kinder
Jeder Knoten hat zwei Zeiger:
einen zu seinem ganz linken Kind, falls es ein solches gibt einen zu seinem rechten Geschwister oder zum Elternknoten, falls er keinen rechten Geschwister hat
Allgemeiner Baum dargestellt als Binärbaum
Praktische Informatik I, HWS 2009, Kapitel 12
Seite 19
Ordnungen von Bäumen
Eine Ordnung (order) ist eine Abbildung eines Baumes auf eine lineare Struktur ("Plattklopfen des Baumes")
Man bildet eine eindeutigen Knotenfolge derart, dass jeder Knoten genau einmal darin vorkommt
Bei Binärbäumen unterscheidet man vier wichtige Ordnungen:
1 A
Die Ordnung gibt an, an welcher Stelle die Wurzel im Bezug zum linken und rechten Unterbaum vorkommen soll.
Preorder Inorder Postorder
Die Definition der Ordnung ist rekursiv.
Beispiel: Preorder
Erst Wurzel, dann linker Teilbaum, dann rechter Teilbaum
Praktische Informatik I, HWS 2009, Kapitel 12
2 B 3 C
5 D 4 E
Preorder: A B C E D
Seite 20
Inorder und Postorder 1. Linker Unterbaum
Regel:
2. Wurzel
2. Rechter Unterbaum
3. Rechter Unterbaum
3. Wurzel
4
5 A
A
2 B 1 C
1. Linker Unterbaum
Regel:
3 B
5 D 3
E
Inorder: C B E A D
Praktische Informatik I, HWS 2009, Kapitel 12
1 C
4 D 2 E
Postorder: C E B D A
Seite 21
Beispiel: Operatorbaum * 5
Inorder: 5 * (((9 + 8) * (4 * 6)) + 7)
+ 7
* *
+ 9
8
4
Klammer auf, wenn man eine Ebene absteigt, Klammer zu, wenn man eine Ebene aufsteigt
6
Postorder: 598+46**7+* Darstellung ohne Klammern trotzdem eindeutig
Der Sytnaxbaum dient zur maschineninternen Darstellung von int-Ausdrücken
Darstellung des Baumes mittels Inorder ergibt unsere vertraute Darstellung
Darstellung des Baumes mittels Postorder ergibt „polnische Notation“
Kann verwendet werden, um mittels eines Stack den Wert des Ausdrucks zu berechnen (siehe Übung)
Praktische Informatik I, HWS 2009, Kapitel 12
Seite 22
Operationen auf rekursiven Daten
Funktionen auf induktiv definierten Daten sind rekursiv am einfachsten
Beispiel : male()
Hilfsfunktion male(int n) Malt einen Baum ab Spalte n
Rote Verbindungslinien nur zur weiteren Hervorhebung
In Blatt: println(info);
In Knoten: rechts.male(n+5); einruecken(n,“+“); links.male(n+5);
Praktische Informatik I, HWS 2009, Kapitel 12
Seite 23
Zusammenfassung
Induktive Datenstrukturen referenzieren sich selbst
Standardbeispiele: Bäume, Listen
Induktive Datenbereiche bearbeitet man natürlich rekursiv
Praktische Informatik I, HWS 2009, Kapitel 12
Seite 24