6. 5. 2011

Werbung
Was bisher geschah
I
I
Algorithmen
Datentypen
I
I
I
I
Abstrakter Datentyp:
(funktionale) Signatur ΣF + Axiome Φ
(Spezifikation)
Konkreter Datentyp:
ΣF -Algebra, die alle Axiome aus Φ erfüllt (d.h. Modell für Φ
ist)
(Realisierung)
rekursive Datenstrukturen
lineare Datenstrukturen:
I
I
I
einfach verkettete Liste
Stack
Queue
111
Wiederholung ADT Liste
ADT Liste ohne Positionszugriff (Spezifikation, Auszug):
Sorten: Bool, Element, Liste (hier kurz für ListhElementi)
Signatur:
nil :
isEmpty :
Liste →
head :
Liste →
tail :
Liste →
contains : Liste × Element →
cons :
Element × Liste →
append :
Liste × Liste →
Liste
Bool
Element
Liste
Bool
Liste
Liste
Axiome . . .
112
Einfach verkettete Liste: rekursive Struktur
induktive Definition:
IA: Die leere Liste ist eine Liste.
IS: Wird (ein Verweis auf) eine Liste an ein Element
angehängt, entsteht eine Liste.
rekursive Struktur einer einfach verketteten Liste L:
ListhElementi = nil | cons ( Element, ListhElementi)
class List<Element> {
Element head;
List<Element> tail;
}
113
Einfach verkettete Liste: Durchquerungen
Durchquerung aller Elemente:
forward( List<Element> x ){
if ( x != null){
...
forward(x.tail)
}
}
backward( List<Element> x ){
if ( x != null){
backward(x.tail)
...
}
}
114
Einfach verkettete Liste: size
rekursiv:
int size( List<Element> x ){
if ( x == null){
return 0;
}
else {
return (1 + x.tail.size());
}
}
oder iterativ:
int size(){
Element x = head;
int s = 0;
while ( x! = null){
x = x.tail;
s++;
}
return s;
}
115
Einfach verkettete Liste: contains
contains(e,x):
boolean contains(Element e, List<Element> x){
if ( x == null){
return false;
}
else {
return (( x.val == e) || contains(e, x.tail));
}
}
116
Einfach verkettete Liste: Laufzeiten
I
isEmpty, head, tail, cons: O(1)
(Test oder Ersetzen von Verweisen am Anfang der Liste)
I
contains O(n)
(Durchlauf aller Verweise bis gefunden)
I
remove O(n)
(Suche und Ersetzen eines Verweises)
I
concat O(n)
(Durchlauf aller Verweise der ersten Liste und Ersetzen
eines Verweises)
117
Wiederholung: Bäume als Graphen
Baum: azyklischer zusammenhängender Graph T = (V , E)
hier außerdem
I
ausgezeichneter Knoten: Wurzel
I
gerichtete Kanten
I
Jeder Knoten hat höchstens einen Vorgänger.
I
Wurzel ist einziger Knoten ohne Vorgänger.
I
festgelegte Ordnung der Nachfolger (Kinder) jedes
Knotens
I
Markierung der Knoten mit Werten, z.B. ∈
N
Blatt: Knoten ohne Kinder (mit 0 Kindern)
innerer Knoten: Knoten mit n > 0 Kindern
118
Eigenschaften von Bäumen
Baum T = (V , E)
|V | = |E| + 1 (Beweis durch Induktion)
minimal zusammenhängend
maximal kreisfrei
I
Zwischen je zwei Knoten existiert genau ein Pfad.
I
Spezialfall: Für jeden Knoten existiert genau ein Pfad zur
Wurzel.
Tiefe eines Knotens = Anzahl der Kanten zur Wurzel
119
Baumstrukturen
Anwendungen z.B. in
I
Gliederung eines Buches (Teile, Kapitel, Abschnitte)
I
Unternehmenstruktur
in der Informatik z.B. in
I
Struktur von Termen, z.B. arithmetische Terme, Formeln
I
Programmstruktur
(Anweisungen, Blöcke, Unterprogramme)
I
Verzeichnisstruktur (Betriebssystem)
I
Prozessstruktur (Betriebssystem)
I
XML-Dokumente
120
Bäume
induktive Definition:
IA: der leere Baum ⊥ ist ein Baum
IS: existiert ein n > 0 und sind t1 , . . . , tn Bäume und v ∈ M,
dann ist auch node(v , (t1 , . . . , tn )) ein Baum
graphische Darstellung (Tafel)
rekursive Struktur eines Baumes:
TreehElementi = ⊥ | Node (Element, ListhTreehElementii)
class Tree<Element> {
Element val;
List<Tree<Element>> children;
121
Rekursive Funktionen auf Bäumen
I
Größe (Anzahl der Knoten)
size(⊥)
size(node(v , (t1 , . . . , tn )))
= 0
= 1+
n
X
size(ti )
i=1
I
Tiefe (Länge des längsten Wurzel-Blatt-Pfades)
height(⊥)
height(node(v , (t1 , . . . , tn )))
=
0
= 1 + max {height(ti ) | i ∈ {1, . . . , n}}
122
Spezialfälle
I
Binärbäume (Grad aller inneren Knoten ∈ {1, 2})
I
vollständige Binärbäume (Grad aller inneren Knoten 2)
I
Unärbäume (Grad aller inneren Knoten 1)
Liste mit size = Länge
123
ADT Binärbaum
mit Knotenmarkierungen vom Typ Element
I
Sorten: Bool, Element, BT (kurz für BinTreehElementi)
I
Signatur:
⊥:
isEmpty :
BT →
Node : Element × BT × BT →
Left, Right :
BT →
Val :
BT →
t, f :
I
BT
Bool
BT
BT
Element
Bool
Axiome, z.B. ∀v ∈ Element, t1 , t2 ∈ BT :
isEmpty(⊥) :
= t
isEmpty(Node(v , t1 , t2 )) :
= f
Left(Node(v , t1 , t2 )) :
= t1
Right(Node(v , t1 , t2 )) :
= t2
Val(Node(v , t1 , t2 )) :
= v
124
Rekursive Baumdurchquerungen (binär)
Reihenfolge des Besuches aller Knoten eines Binärbaumes
(z.B. zum Durchsuchen)
BinTreehElementi = ⊥
| Node (Element, BinTreehElementi, BinTreehElementi)
Preorder
preorder(Node(v , l, r )) = v ◦preorder(l)◦preorder(r )
bei Ausgabe: v als Präfix
Postorder
postorder(Node(v , l, r )) = postorder(l)◦postorder(r )◦v
bei Ausgabe: v als Postfix
Inorder
inorder(Node(v , l, r )) = inorder(l) ◦ v ◦ inorder(r )
bei Ausgabe: v als Infix
125
Rekursive Baumdurchquerungen (allgemein)
TreehElementi = ⊥ | Node (Element, ListhBinTreehElementii)
Preorder: preorder(Node(v , [t1 , . . . , tn ])) =
v ◦ preorder(t1 ) ◦ · · · ◦ preorder(tn )
bei Ausgabe: v als Präfix
Postorder: postorder(Node(v , [t1 , . . . , tn ])) =
postorder(t1 ) ◦ · · · ◦ postorder(tn ) ◦ v
bei Ausgabe: v als Postfix
Inorder meist nicht sinnvoll
126
Rekursive Funktionen auf Bäumen
Anzahl der Knoten:
int size( BinTree<Element> x ){
if ( x == null){
return 0;
}
else {
return (1 + size(x.left) + size(x.right));
}
}
Tiefe:
int height( BinTree<Element> x ){
if ( x == null){
return 0;
}
else {
return (1 + max(size(x.left), size(x.right)));
}
}
127
Binärbäume: contains
boolean contains(Element e, BinTree<Element> x){
if ( x == null){
return false;
}
else {
return ( ( x.val == e)
|| contains(e, x.left)
|| contains(e, x.right));
}
}
128
Vollständige Binärbäume
BinTree<int> fullBinTree(int n){
if ( n=0 ) {
return nil;
}
else {
return ( Node (n, fullBinTree(n-1) ,
fullBinTree(n-1) ));
}
}
129
Nichtrekursive Baumdurchquerungen
(für Bäume beliebiger Knotengrade)
Zwischenspeicher notwendig zur Verwaltung der
Menge La der noch zu besuchenden Knoten
Allgemeiner Durchquerungs-Algorithmus:
1. La = {s} (Wurzel s des zu durchquerenden Baumes)
2. solange La 6= ∅ wiederhole:
Auswahl und Entfernen eines Knotens u aus La
Einfügen aller Kinder von u in La
130
Nichtrekursive Baumdurchquerungen
Datenstruktur für Zwischenspeicher La (entdeckte, aber noch
nicht besuchte Knoten) bestimmt Durchquerungsreihenfolge.
Tiefensuche
Verwaltung von La als Stack
Einfügen der Nachbarn: push
I Auswahl des Knotens, der zuletzt in La
eingefügt wurde (top / pop)
(gleiche Reihenfolge wie preorder-Durchquerung)
I
I
Breitensuche
Verwaltung von La als Queue
Einfügen der Nachbarn: enqueue
I Auswahl des Knotens, der zuerst in La
eingefügt wurde (dequeue)
(level-order-Durchquerung)
I
I
131
Herunterladen