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
Arrays
einfach verkettete Liste
ADT Stack, ADT Queue
rekursive Datenstrukturen (Listen)
113
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
114
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
115
Bäume
induktive Definition der Menge TreehEi aller Bäume mit
Elementen (in den inneren Knoten) aus E:
IA: der leere Baum Leaf ist ein Baum (Leaf ∈ TreehEi)
IS: existiert ein n > 0 und sind t1 , . . . , tn Bäume
(∀i ∈ {1, . . . , n} : ti ∈ Leaf ∈ TreehEi)
und k ∈ E,
dann ist auch Branch(k , (t1 , . . . , tn )) ein Baum
(Branch(k , (t1 , . . . , tn )) ∈ TreehEi)
graphische Darstellung (Tafel)
rekursive Struktur:
TreehEi = {Leaf} ∪ (E × (TreehEi)∗ )
116
Spezialfälle
Binärbäume (Kinderzahl aller inneren Knoten 2):
induktive Definition der Menge BinTreehEi aller
Binärbäume mit Elementen (in den inneren
Knoten) aus E:
IA: Leaf ∈ BinTreehEi)
IS: left, right ∈ BinTreehEi ∧ key ∈ E
→ Branch(key, left, right) ∈ BinTree
rekursive Struktur:
BinTreehEi = {Leaf}∪(E×BinTreehEi×BinTreehEi)
(Operationen key, left, right zum Zugriff auf diese
Komponenten)
Unärbäume (Kinderzahl aller inneren Knoten 1)
Liste mit size = Länge
117
ADT Binärbaum
mit Knotenmarkierungen vom Typ Element
I
Sorten: Bool, E, BT (kurz für BinTreehEi)
I
Signatur:
Leaf :
isEmpty :
BT →
Branch : E × BT × BT →
left, right :
BT →
key :
BT →
t, f :
I
BT
Bool
BT
BT
E
Bool
Axiome, z.B. ∀k , j ∈ E mit k 6= j und l, r ∈ BT :
isEmpty(Leaf) = t
isEmpty(Branch(k , l, r )) = f
left(Branch(k , l, r )) = l
right(Branch(k , l, r )) = r
contains(Branch(k , l, r ), k ) = t
key(Branch(k , l, r )) = k
contains(Branch(k , l, r ), j)
=
contains(l, j) ∨ contains(r , j)
...
118
Wiederholung: Länge einer Liste
(Anzahl der Speicherpositionen)
rekursiver Algorithmus:
Algorithmus : size
Eingabe : l ∈ ListhEi
Ausgabe : n ∈
N
wenn l = Nil dann
n←0
sonst
n ← 1 + size(tail(l))
119
Größe eines Binärbaumes
(Anzahl der Speicherpositionen)
rekursiver Algorithmus:
Algorithmus : size
Eingabe : t ∈ BinTreehEi
Ausgabe : n ∈
N
wenn t = Leaf dann
n←0
sonst
n ← 1 + size(left(t)) + size(right(t))
120
Weitere rekursive Funktionen auf Binärbäumen
I
Tiefe (Länge des längsten Wurzel-Blatt-Pfades)
height(Leaf) = 0
height(Branch(k , l, r )) = 1 + max (height(l), height(r ))
I
Menge aller gespeicherten Werte (Inhalt)
I
Anzahl aller Vorkommen eines bestimmten Wertes
I
für Bäume über geordneten Mengen (E, ≤):
minimales, maximales Element
I
Menge aller Teilbäume
121
Rekursive Durchquerungen von Binärbäumen
Reihenfolge des Besuches aller Knoten eines Binärbaumes
(z.B. zum Durchsuchen)
Preorder
preorder(Branch(k , l, r )) = k ◦preorder(l)◦preorder(r )
bei Ausgabe: Wurzel als Präfix
Postorder
postorder(Branch(k , l, r )) = postorder(l)◦postorder(r )◦k
bei Ausgabe: Wurzel als Postfix
Inorder
inorder(Branch(k , l, r )) = inorder(l) ◦ k ◦ inorder(r )
bei Ausgabe: Wurzel als Infix
ÜA: analoge Durchquerungen für allgemeine Bäume
122
Implementierung des ADT Menge durch Binärbäume
Operationen: (Emptyset, isEmpty,) contains, add, remove
Spezifikation der Funktion contains (auf ADT Menge):
V: Eingabe Menge M ∈ MengehEi,
x ∈E
N:
I
I
t, falls x ∈ M,
f, sonst
Realisierung des ADT Menge durch Binärbäume:
Spezifikation der Funktion contains (auf Binärbäumen):
V: Eingabe Binärbaum t ∈ BinTreehEi
x ∈E
N:
I
I
t, falls t einen Knoten mit Wert x enthält
(t also einen Teilbaum Branch(k , l, r ) mit
k = x enthält) existiert
f, sonst
123
contains auf Binärbäumen
Bezeichnung: für t ∈ BinTreehEi ist die Menge aller in t
gespeicherten Werte Inhalt(t) ⊆ 2E definiert durch
I
Inhalt(Leaf) = ∅
I
Inhalt(Branch(k , l, r )) = {k } ∪ Inhalt(l) ∪ Inhalt(r )
Algorithmus : contains
Eingabe : t ∈ BinTreehEi, e ∈ E
Ausgabe : b ∈ {t, f} mit b = t gdw. e ∈ Inhalt(t)
wenn t = Leaf dann b ← f
sonst wenn e = key(t) dann b ← t
sonst b ← contains(left(t), e) ∨ contains(right(t), e)
oder kürzer:
contains(t, e) ←
t 6= Leaf ∧ (e = key(t) ∨ contains(left(t)) ∨ contains(right(t)))
124
add auf Binärbäumen
ein möglicher Algorithmus (Einfügen links unten):
Algorithmus : add
Eingabe : t ∈ BinTreehEi, e ∈ E
Ausgabe : t 0 ∈ BinTreehEi mit Inhalt(t 0 ) = Inhalt(t) ∪ {e}
wenn l = Leaf dann
t 0 ← Branch(e, Leaf, Leaf)
sonst
t 0 ← Branch(key(t), add(left(t), e), right(t))
Laufzeit für Baum t mit n Knoten: O(height(t))
best case O(log n), worst case O(n)
remove?
für Teilbäume Branch(e, l, r ) mit l = Leaf oder r = Leaf einfach,
für andere Knoten schwieriger (später)
125
Binäre Suchbäume
Der Binärbaum t hat die Suchbaum-Eigenschaft gdw.
für jeden Teilbaum Branch(x, l, r ) von t gilt:
I
für jeden Schlüsselwert y eines inneren Knotens von l gilt
y <x
I
für jeden Schlüsselwert y eines inneren Knotens von r gilt
x <y
Ergebnis der Inorder-Durchquerung jedes binären
Suchbaumes ist eine aufsteigend sortierte Folge
Laufzeit für sortierte Ausgabe aller im Suchbaum mit n Knoten
enthaltenen Schlüssel: O(n)
126
Binäre Suchbäume: contains
Spezifikation der Funktion contains (auf binärem Suchbaum):
V: Eingabe t ∈ BinTreehEi mit Suchbaum-Eigenschaft, e ∈ E
N: Ausgabe t, falls t einen Teilbaum Branch(e, l, r ) enthält
f, sonst
Idee (rekursiv):
IA: Der leere Baum Leaf enthält keinen Schlüssel.
IS: Baum Branch(k , l, r ) enthält e falls
I
I
I
e = k oder
e < k und linkes Kind enthält e oder
e > k und rechtes Kind enthält e
127
Binäre Suchbäume: contains
Algorithmus : contains
Eingabe : t ∈ BinTreehEi mit Suchbaum-Eigenschaft, e ∈ E
Ausgabe : b ∈ {t, f} mit b = t gdw. e ∈ Inhalt(t)
wenn l = Leaf dann b ← f
sonst wenn e = key(t) dann b ← t
sonst wenn e < key(t) dann b ← contains(left(t), e)
sonst b ← contains(right(t), e)
128
Laufzeit der Suche
Laufzeit von contains(t, e) hängt von der Mächtigkeit N der
Menge (Anzahl der inneren mitKnoten in t) ab:
I
falls t einen Teilbaum Branch(e, l, r ) enthält:
Länge des Pfades von Wurzel zu Teilbaum
I
falls t keinen Teilbaum Branch(e, l, r ) enthält:
Länge eines längsten Wurzel-Blatt-Pfades (Höhe des
Baumes t)
höchstens Höhe des Baumes t (log(n) ≤ height(t) ≤ n)
Suche im Suchbaum t in O(height(t))
(i.A. schneller als lineare Suche in einer Folge)
129
Extremwerte in binären Suchbäumen
Spezifikation Minimum:
V: Eingabe binärer Suchbaum t ∈ BinTreehEi
über total geordneter Menge E
N: Ausgabe m: minimaler in t vorkommender Schlüsselwert
(undefiniert für leeren Baum)
Idee: in jedem binären Suchbaum steht der minimale Schlüsselwert
im äußeren linken Knoten
(Minimum lässt sich ohne Schlüsselvergleich bestimmen.)
Algorithmus : minimum
Eingabe : t ∈ BinTreehEi mit Suchbaum-Eigenschaft
Ausgabe : m ∈ E mit ∀e ∈ Inhalt(t) : m ≤ e
wenn t = Leaf dann m ← undefiniert
sonst wenn t = Branch(k , Leaf, r ) dann m ← k
sonst m ← minimum(left(t))
Laufzeit höchstens Tiefe des Baumes t (log(n) ≤ height(t) ≤ n)
Maximum analog (äußerer rechter Knoten)
130
Einfügen in binäre Suchbäume
Spezifikation add:
V: Eingabe t ∈ BinTreehEi mit Suchbaum-Eigenschaft, e ∈ E
N: Ausgabe t 0 ∈ BinTreehEi mit Suchbaum-Eigenschaft und
Inhalt(t 0 ) = Inhalt(t) ∪ {e}
Idee (rekursiv):
IA: Einfügen in leeren Baum durch Erzeugung eines Knotens
IS: in jedem Teilbaum:
I
I
I
schon vorhandene Schlüssel nicht einfügen (Menge)
Schlüssel < Wurzelschlüssel in linkes Kind einfügen
Schlüssel > Wurzelschlüssel in rechtes Kind einfügen
131
Einfügen in binäre Suchbäume
Algorithmus : add
Eingabe : t ∈ BinTreehEi mit Suchbaum-Eigenschaft, e ∈ E
Ausgabe : t 0 ∈ BinTreehEi mit Suchbaum-Eigenschaft und
Inhalt(t 0 ) = Inhalt(t) ∪ {e}
wenn t = Leaf dann t 0 ← Branch(e, Leaf, Leaf)
sonst wenn e = key(t) dann t 0 ← t
sonst wenn e < key(t) dann t 0 ← add(left(t), e)
sonst t 0 ← add(right(t), e)
132
Iteriertes Einfügen
Beim Einfügen der Elemente der Menge {2, 3, 5, 7} in
verschiedenen Reihenfolgen in leeren Baum entstehen i.A.
veschiedene Bäume
Beispiel (Tafel):
I
3,7,5,2
I
5,3,7,2
I
2,3,5,7
Daraus lässt sich ein Sortierverfahren ableiten.
Sortieren durch Einfügen in binären Suchbaum:
1. schrittweises Einfügen aller Elemente einer Menge in
einen zu Beginn leeren binären Suchbaum
2. Sortierte Ausgabe durch Inorder-Durchquerung des so
entstandenen binären Suchbaumes
133
Laufzeit Einfügen
Idee:
Schlüsselwert e wird dort in t eingefügt, wo ihn der
Suchalgorithmus (contains) in t finden würde.
I
falls e schon vorhanden: kein Einfügen
I
falls e noch nicht vorhanden (Blatt):
Ersetzen des Blattes durch neuen Knoten
Branch(e, Leaf, Leaf)
also dieselbe Laufzeit wie contains:
höchstens Höhe des Baumes t (log(n) ≤ height(t) ≤ n)
Einfügen in einen binären Suchbaum mit n Knoten in
O(height(t))
Laufzeit des abgeleiteten Sortierverfahrens: O(n height(t))
134
Entfernen aus binären Suchbäumen
Spezifikation remove:
V: Eingabe t ∈ BinTreehEi mit Suchbaum-Eigenschaft, e ∈ E
N: Ausgabe t 0 ∈ BinTreehEi mit Suchbaum-Eigenschaft und
Inhalt(t 0 ) = Inhalt(t) \ {e}
mögliche Fälle:
1. e kommt nicht in t vor: Ergebnis t 0 = t
2. e ist Schlüsselwert eines Teilbaumes s in t mit zwei leeren
Kindern: Löschen des Knotens s (Ersetzen durch Leaf)
3. e ist Schlüsselwert eines Knotens s in t mit einem leeren Kind:
Ersetzen des Knotens s durch sein einziges nichtleeres Kind
4. e ist Schlüsselwert eines Knotens s in t mit zwei nichtleeren
Kindern:
Tausch der Schlüsselwerte in s und dem linken äußeren Knoten
r des rechten Kindes von s (Minimum des rechten Kindes)
Löschen des Knotens r (rekursiv)
Warum hat der so entstandene Baum die Suchbaumeigenschaft?
Laufzeit für Löschen aus binärem Suchbaum t: O(height(t))
135
Binäre Suchbäume: remove
Algorithmus : remove
Eingabe : t ∈ BinTreehEi mit Suchbaum-Eigenschaft, e ∈ E
Ausgabe : t 0 ∈ BinTreehEi mit Inhalt(t 0 ) = Inhalt(t) \ {e}
wenn t = Leaf dann t 0 ← Leaf
sonst wenn e = key(t) dann
wenn left(t) = Leaf dann t 0 ← remove(right(t), e)
sonst wenn right(t) = Leaf dann t 0 ← remove(left(t), e)
sonst t 0 ←
Branch(minimum(right(t)), l, remove(right(t), minimum(right(t))))
sonst wenn e < key(t) dann
t 0 ← Branch(key(t), remove(left(t), e), right(t))
sonst
t 0 ← Branch(key(t), left(t), remove(right(t), e))
136
Laufzeiten in binären Suchbäumen
I
contains
I
add
I
remove
in O(height(t))
Laufzeiten in Abhängigkeit von der Knotenzahl n = size(t):
Extremfälle:
I
für Pfade (entartete Bäume) height(t) = size(t)
Laufzeit der Operationen O(n)
I
für balancierte Bäume height(t) = log size(t)
Laufzeit der Operationen O(log(n))
137
Anwendung von Suchbäumen
(Balancierte) binäre Suchbäume sind geeignet zum
I
Speichern,
I
schnellen Wiederfinden
I
Entfernen,
I
sortierten Augeben
von Daten anhand ihnen zugeordneten Schlüsselwerten
(Schlüsselwerte müssen total geordnete Menge bilden)
I
Realisierung des ADT Menge für linear geordnete Mengen
I
Realisierung von Wörterbüchern
(= Mengen von Paaren (Schlüssel, Wert) mit eindeutigem
Schlüssel aus einer linear geordneten Menge, z.B. ⊆ )),
(= partielle Funktionen: Schlüsselbereich → Wertebereich):
Operationen: contains, add, remove, jeweils von Paaren
(Schlüssel, Daten)
z.B. Telefonbucheinträge über Namen (alphabetisch geordnet),
Studenten über Studentennummern
N
138
Balancierte binäre Suchbäume
Laufzeit für Suche, Einfügen, Löschen in Baum t mit n Knoten:
O(height(t))
Ziel: Laufzeit für Suche, Einfügen, Löschen O(log n)
Idee: balancierte Suchbäume, in denen die Tiefe jedes
Blattes (etwa) gleich ist (also O(log n))
139
Vollständig balancierte Bäume
Binärer Suchbaum t heißt vollständig balanciert, wenn in jedem
Knoten Branch(x, l, r ) in t gilt:
|size(l) − size(r )| ≤ 1
Tiefe jedes Knotens ≤ blog(n)c + 1
Beispiel (Tafel)
140
Operationen in vollständig balancierten Bäumen
Emptyset O(1)
isEmpty O(1)
contains O(log(n))
add, remove in je zwei Schritten:
1. Einfüge- und Löschoperation für balancierte
binäre Suchbäume: O(log(n))
2. Rebalancieren, d.h. Wiederherstellen der
vollständigen Balance: i.A. aufwendig
141
Balancierte Suchbäume
Problem: (Zeit-)Aufwand zum Herstellen der vollständigen
Balanche nach Einfügen und Löschen zu hoch (> O(log n))
Verschiedene Lösungsansätze zur Realisierung der
Operationen contains, add, remove
in Laufzeit O(log n):
I
Binärbäume mit schwächerer (aber ausreichender)
Balance-Bedingung, die nach Einfügen und Löschen in
O(log n) Schritten wiederherstellbar ist
(daher Erhalt der Laufzeit der Operationen), z.B.
I
I
I
AVL-Bäume
Rot-Schwarz-Bäume
Mehrweg-Suchbäume:
vollständig balancierte Bäume mit verschiedenen
Knotengraden, z.B.
I
I
2-3-Bäume (innere Knoten mit 2 oder 3 Kindern)
B-Bäume (innere Knoten mit zwischen n/2 und n Kindern)
142
Herunterladen