Algorithmen und Datenstrukturen

Werbung
Was bisher geschah
I
Algorithmen
I
Datentypen
I
I
Abstrakter Datentyp (Spezifikation):
(funktionale) Signatur ΣF + Axiome Φ
Konkreter Datentyp (Realisierung, Implementierung):
ΣF -Algebra, die alle Axiome aus Φ erfüllt
(d.h. Modell für Φ ist)
I
ADT Menge
I
lineare Datenstrukturen:
I
ADT Folge (mit und ohne Positionszugriff)
Realisierungen:
I
I
I
I
I
Arrays
einfach verkettete Liste
doppelt verkettete Liste
ADT Stack, ADT Queue
rekursive Datenstrukturen (Listen)
29
Wiederholung ADT
ADT für Himmelsrichtungen Norden, Osten, Süden, Westen
mit Operationen für Rechts- und Linksabbiegen und Umlenken
Spezifikation des ADT Himmelsrichtungen:
Sorten: Himmelsrichtung (HR) = {N, O, S, W}
Signatur:
N, O, S, W :
rechts, links, um : HR →
HR
HR
Axiome:

rechts(N) = O, links(N) = W, um(N) = S, . . .



∀h ∈ HR : rechts(links(h)) = h,
∀h
∈ HR : links(rechts(h)) = h,



∀h ∈ HR : rechts(rechts(h)) = um(h), . . .







in Haskell:
data hr = N | O | S | W
rechts :: hr -> hr
rechts N = O
...
30
Wiederholung ADT Folge
ADT Folge ohne Positionszugriff (Spezifikation, Auszug):
Sorten: Bool, Element, Folge (hier kurz für FolgehElementi)
Signatur:
nil :
head :
Folge →
tail :
Folge →
contains : Folge × Element →
cons :
Element × Folge →
append :
Folge × Folge →
isEmpty :
Folge →
Folge
Element
Folge
Bool
Folge
Folge
Bool
Axiome isEmpty(nil) = t, ∀x∀lisEmpty(cons(x, l)) = f, . . .
Beispiel (Sorten und Signatur): Java-Interface List
public interface List {
Element head ();
List tail ();
...
}
31
Einfach verkettete Liste: rekursive Struktur
induktive Definition einer einfach verketteten Liste von
Elementen vom Typ a (polymorph):
IA: Die leere Liste ist eine Liste von Elementen vom
Typ a.
IS: Ist e ein Element vom Typ a und l eine Liste von
Elementen vom Typ a,
dann ist auch das Paar (e, l) eine Liste von
Elementen vom Typ a.
rekursiver Datentyp einfach verkettete Liste von Elementen
vom Typ a:
in Haskell:
data List a = Nil {}
| Cons { head :: a, tail :: List a }
oft kürzer (Nil 7→ [], Cons 7→ :, ohne Komponentennamen):
data [a] = [] | a : [a]
32
Einfach verkettete Liste: Durchquerungen
rekursive Durchquerungen aller Elemente in Haskell:
forward :: [a] -> [a]
forward l = case l of
[]
-> []
(x : xs) -> x : (forward xs)
backward :: [a] -> [a]
backward l = case l of
[]
-> []
(x : xs) -> (backward xs) ++ [x]
33
Einfach verkettete Liste: Durchquerungen
rekursive Durchquerungen aller Elemente in Java:
forward( List<Element> x ){
if ( x != null){
...
forward(x.tail)
}
}
backward( List<Element> x ){
if ( x != null){
backward(x.tail)
...
}
}
34
Einfach verkettete Liste: size
(rekursive Berechnung der Länge der Liste)
in Haskell:
size :: [a] -> Int
size l = case l of
[]
-> 0
(x : xs) -> 1 + (size xs)
in Java:
int size( List<Element> x ){
if ( x == null){
return 0;
}
else {
return (1 + x.tail.size());
}
}
35
Einfach verkettete Liste: contains
in Haskell:
contains :: Eq a => [a] -> a -> Bool
contains l y = case l of
[]
-> False
(x : xs) -> (x == y) || (contains xs y)
in Java:
boolean contains(List<A> x, A y){
if ( x == null){
return false;
}
else {
return (( x.val == y) || contains(x.tail, y));
}
}
36
Einfach verkettete Liste: remove
Entfernen aller Vorkommen eines Elementes aus einer Liste
in Haskell:
remove :: Eq a => [a] -> a -> [a]
remove l y = case l of
[]
-> []
(x : xs) -> if (x == y) then (remove xs y)
else x : (remove xs y)
37
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 von Verweisen bis Ende)
I
concat O(n)
(Durchlauf aller Verweise der ersten Liste und Ersetzen
eines Verweises)
38
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
39
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
40
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
41
Bäume
induktive Definition:
IA: der leere Baum Leaf ist ein Baum
IS: existiert ein n > 0 und sind t1 , . . . , tn Bäume und v ∈ M,
dann ist auch Branch(k , (t1 , . . . , tn )) ein Baum
graphische Darstellung (Tafel)
rekursive Struktur eines Baumes:
in Haskell:
data Tree a
= Leaf
| Branch { key :: a, sons :: [Tree a] }
in Java:
class Tree<A> {
A val;
List<Tree<A>> sons;
}
42
Spezialfälle
I
Binärbäume (Grad aller inneren Knoten 2)
I
Unärbäume (Grad aller inneren Knoten 1)
Liste mit size = Länge
43
ADT Binärbaum
mit Knotenmarkierungen vom Typ Element
I
Sorten: Bool, Element, BT (kurz für BinTreehElementi)
I
Signatur:
Leaf :
isEmpty :
BT →
Branch : Element × BT × BT →
left, right :
BT →
key :
BT →
add :
BT × Element →
contains :
BT × Element →
t, f :
I
BT
Bool
BT
BT
Element
BT
Bool
Bool
Axiome, z.B. ∀k ∈ Element, l, r ∈ BT :
isEmpty(Leaf) :
= t
isEmpty(Branch(k , l, r )) : = f
left(Branch(k , l, r )) : = l
right(Branch(k , l, r )) :
= r
key(Branch(k , l, r )) :
= k
44
Rekursive Funktionen auf Binärbäumen
data BinTree a = Leaf
| Branch {key::a, left::BinTree a, right::BinTree a}
deriving Show
I
Größe (Anzahl der Knoten)
size :: BinTree a -> Int
size t = case t of
Leaf
-> 0
(Branch k l r) -> 1 + (size l) + (size r)
I
Höhe (Länge des längsten Wurzel-Blatt-Pfades)
height :: BinTree a -> Int
height t = case t of
Leaf
-> 0
(Branch k l r) -> 1 + max (height l) (height r)
45
Rekursive Durchquerungen von Binärbäumen
Reihenfolge des Besuches aller Knoten eines Binärbaumes
data BinTree a = Leaf
| Branch {key::a, left::BinTree a, right::BinTree a}
Preorder preorder :: BinTree a -> [a]
preorder t = case t of Leaf -> []
Branch k l r -> [k] ++ (preorder l)
++ (preorder r)
bei Ausgabe: Wurzel als Präfix
Postorder postorder :: BinTree a -> [a]
postorder t = case t of Leaf -> []
Branch k l r -> (postorder l)
++ (postorder r) ++ [k]
bei Ausgabe: Wurzel als Postfix
Inorder inorder :: BinTree a -> [a]
inorder t = case t of Leaf -> []
Branch k l r -> (inorder l) ++ [k]
++ (inorder r)
bei Ausgabe: Wurzel als Infix
46
Vollständige Binärbäume
praktisch zur Erzeugung von Beispielen und Testen der
Funktionen:
Erzeugung vollständiger Binärbäume beliebiger Höhe
Spezifikation der Funktion fullBinTree:
V: Eingabe n ∈
N
N: falls n > 0: vollständiger Binärbaum der Höhe n,
sonst ⊥
in Haskell:
fullBinTree :: Int -> BinTree Int
fullBinTree n = if n <= 0 then Leaf
else Branch n (fullBinTree (n-1))
(fullBinTree (n-1))
47
Binärbäume: contains
Spezifikation der Funktion contains:
V: Eingabe (x1 , . . . , xn ) ∈ A∗ , y ∈ A
N: ja, falls ∃i ∈ {1, . . . , |x|} : y = xi
nein, sonst
in Haskell:
contains :: Eq a =>
contains t x = case
Leaf
->
(Branch k l r) ->
BinTree a -> a -> Bool
t of
False
(k == x)
|| contains l x
|| contains r x
48
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
49
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 (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
50
Herunterladen