Kapitel 12: Induktive

Kapitel 12: Induktive Datenstrukturen Felix Freiling Lehrstuhl für Praktische Informatik 1 Universität Mannheim Vorlesung Praktische Informatik I im H...
Author: Heike Weiner
15 downloads 2 Views 2MB Size
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