Kapitel 12: Induktive Datenstrukturen

Werbung
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<E>
{
// Objektfelder:
private E inhalt;
private Liste rest;
ein erstes Element
und eine Rest-Liste
Objektreferenz null
bedeutet leere Liste
new Liste<E>(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<E> {
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<E>
{
// 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
Herunterladen