Informatik 2

Werbung
Informatik 2
Informatik 2
Dipl.-Inf. Domenic Jenz ([email protected])
http://informatikdhs.npage.de/
Ab 30. April 2013
Informatik 2
Einleitung
Termine
1. 30.4.2013
7. 11.6.2013
2. 7.5.2013
8. 18.6.2013
3. 14.5.2013
9. 25.6.2013
4. 21.5.2013
10. 2.7.2013
5. 28.5.2013
11. 9.7.2013 (Klausur?)
6. 4.6.2013
12. 16.7.2013 (Klausur?)
Informatik 2
Aufwandsabschätzung
Landau-Symbole
Landau-Symbole
I
Mit fleißigem Zählen kann man oft die Anzahl an
Operationen als Funktion f (n) in Abhängigkeit von der
Größe der Eingabedaten aufstellen.
I
Häufig interessiert aber nicht die genaue Anzahl an
Operationen, sondern das asymptotische
Laufzeitverhalten.
I
Dieses wird formal in den Landau-Symbolen erfasst.
I
Definition von ein paar der Interessantesten:
f ∈ O(g) ⇔ ∃ c > 0 ∃ x0 > 0 ∀x > x0 : |f (x)| ≤ c · |g(x)|
f ∈ Ω(g) ⇔ g ∈ O(f )
f ∈ Θ(g) ⇔ f ∈ O(g) ∧ g ∈ O(f )
Informatik 2
Aufwandsabschätzung
Landau-Symbole
Häh ?!!!
Die Definitionen etwas anders:
I
f ∈ O(g): f wächst langsamer oder ungefähr gleich schnell
wie g.
Bei Polynomen: Grad von f ist höchstens so groß wie Grad
von g.
f (n)
limn→∞ g(n)
existiert.
I
f ∈ Ω(g): f wächst schneller oder ungefähr gleich schnell
wie g
Bei Polynomen: Grad von f ist mindestens so groß wie
Grad von g.
I
f ∈ Θ(g): f wächst ungefähr genau so schnell wie g
Bei Polynomen: der Grad von f ist gleich dem Grad von g.
f (n)
limn→∞ g(n)
= c, c 6= 0
Informatik 2
Aufwandsabschätzung
Landau-Symbole
Beispiele
f(n)
7 log(n) + 42
2n
n log(4n) + n
1 2
2 n + log n
n3 − 4n2
2n
5n
7sin2 (n)
√
n n
O(n)
+
+
+
-
O(n log n)
+
+
+
+
-
O(n2 )
+
+
+
+
+
+
O(en )
+
+
+
+
+
+
+
+
Ω(n2 )
+
+
+
+
-
Θ(n2 )
+
-
Informatik 2
Aufwandsabschätzung
Landau-Symbole
Was bedeutet also nun eine Verdoppelung der Problemgröße
für die Laufzeit der Programme ?
f (n) ∈ O(log(n)) : f(n) = 100 → f (2n) ≈ 101
→ f (8n) ≈ 108
f (n) ∈ O(n) :
f(n) = 100 → f (2n) ≈ 200
→ f (8n) ≈ 800
f (n) ∈ O(n2 ) :
f(n) = 100 → f (2n) ≈ 400
→ f (8n) ≈ 6400
f (n) ∈ O(n3 ) :
f(n) = 100 → f (2n) ≈ 800
→ f (8n) ≈ 51200
f (n) ∈ O(2n ) :
f(n) = 100 → f (2n) ≈ 10000
→ f (8n) ≈ 1016
Bitte beachten: Die Größe der Probleme, die hier in 100
Zeiteinheiten berechnet werden kann ist natürlich
unterschiedlich und wird mit jeder Zeile kleiner !
Informatik 2
Aufwandsabschätzung
Beispielprobleme
Code 1
1
2
3
4
5
6
7
8
9
10
11
12
int getMax (int field[], int n)
{
int tempMax = field[0];
for (int i = 1; i < n; i++)
{
if (tempMax < field[i])
{
tempMax = field[i];
}
}
return tempMax;
}
Laufzeitkomplexität ist in O(n)
Informatik 2
Aufwandsabschätzung
Beispielprobleme
Code 2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
int getMaxProd (int field[], int n)
{
int tempMax = field[0]*field[1];
for (int i = 0; i < n; i++)
{
for (int j = i+1; j < n; j++)
{
if (tempMax < field[i]*field[j])
{
tempMax = field[i] * field[j];
}
}
}
return tempMax;
}
Laufzeitkomplexität ist in O(n2 )
Informatik 2
Aufwandsabschätzung
Beispielprobleme
Code 3
1
2
3
4
5
6
7
8
9
10
11
12
13
14
void differentiate2 (int nodes[],int result[],int size)
{
int weights[3] = {1,-2,1};
result[0] = weights[1] * nodes[0]
+ weights[2] * nodes[1];
result[size-1] = weights[0] * nodes[size-2]
+ weights[1] * nodes[size-1];
for (int i = 1; i < size-1; i++){
result[i] = 0;
for (int j = -1; j <= 1; j++){
result[i] += weights[j+1] * nodes[i+j];
}
}
}
Laufzeitkomplexität ist in O(n)
Informatik 2
Aufwandsabschätzung
Beispielprobleme
Code 4
1
2
3
4
5
6
7
8
9
10
11
int addRek (int values[], int start, int size)
{
if (size == 1){
return values[start];
} else{
int size1 = size / 2;
int size2 = size - size1;
return addRek (values, start, size1)
+ addRek (values, start + size1, size2);
}
}
Laufzeitkomplexität
ist O(n) Anfangen mit
f (n) = f n2 + f n2 + 1 mit f (1) = 0, dann die Vermutung
f (n) = n − 1 per Induktion beweisen.
Informatik 2
Aufwandsabschätzung
Mastertheorem
Mastertheorem
(Aus Uwe Schöning: Algorithmik)
Gegeben sei eine Rekursiongleichung der Form
T (n) =
m
X
T (αi n) + Θ(nk ),
0 < αi < 1, m ≥ 1, k ≥ 0
i=1
Dann kann T (n) asymptotisch wie folgt abgeschätzt werden:

Pm k
,
αi < 1
 Θ(nk )
Pi=1
m
k
T (n) ∈
Θ(nk log n) ,
i=1 αi = 1
P

m
k
Θ(nc )
,
i=1 αi > 1
dabei ist c die Lösung zu
Pm
c
i=1 αi
= 1.
Informatik 2
Aufwandsabschätzung
Mastertheorem
Mastertheorem speziell
Häufig sind die αi gleich (d.h. das Feld wird gleichmäßig
zerteilt):
T (n) = m · T (αn) + Θ(nk ),
0 < α < 1, m ≥ 1, k ≥ 0
Jetzt kann T (n) asymptotisch wie folgt abgeschätzt werden:

, m · αk < 1
 Θ(nk )
k
T (n) ∈
Θ(n log n) , m · αk = 1

Θ(nc )
, m · αk > 1
m
In dem Fall vereinfacht sich der letzte Teil: c = − ln
ln α
Informatik 2
Aufwandsabschätzung
Mastertheorem
Beispiele
I
T (n) = 2 · f ( n2 ) + 1 ⇒ T (n) ∈ Θ(n1 ), mit
ln 2
m = 2, α = 12 , k = 0 und somit 3.Fall mit c = − ln (1/2)
=1
I
T (n) = 8 · f ( n3 ) + n2 ⇒ T (n) ∈ Θ(n2 ), mit
m = 8, α = 13 , k = 2 und somit 1.Fall
I
T (n) = 9 · f ( n3 ) + n2 ⇒ T (n) ∈ Θ(n2 log n), mit
m = 9, α = 13 , k = 2 und somit 2.Fall
Informatik 2
Suchen und Bäume
Einleitung
Suchalgorithmen
I
Ziel ist es aus einer Menge von Daten ein oder mehrere
Elemente anhand eines Schlüssels zu finden
I
Ist die Menge (Array / verkettete Liste) unsortiert, so bleibt
uns nichts anderes übrig als alle Elemente anzuschauen
→ O(n)
I
Bei sortierten Mengen kann man evtl. schon früher
abbrechen, aber muß evtl. trotzdem bis ans Ende suchen
I
Hat man direkten Zugriff auf die Elemente (Arrays) geht es
noch besser, über Intervallschachtelung.
I
Wir suchen und zeigen im Folgenden zur Vereinfachung
nur die Schlüssel, welche Integerwerte sein werden
Informatik 2
Suchen und Bäume
Einleitung
Binäre Suche
I
I
Es soll in einer Menge mit n Elementen gesucht werden.
Wir schauen uns das mittlere Element an:
I
I
I
I
Ist der gesuchte Wert kleiner als das mittlere Element,
dann beschaue im nächsten Schritt nur noch die Menge
links von m
Ist der gesuchte Wert größer, dann nimm im nächsten
Schritt die rechte Teilmenge
(evtl.) ist es der gesuchte Wert, so wurde die Stelle schon
gefunden
Mit dem Mastertheorem sieht man, daß die Laufzeit in
O(log n) liegt.
Informatik 2
Suchen und Bäume
Einleitung
Binäre Suche Code 1
function B IN S EARCH(M, start, ende, key )
if ende ≥ start then
mitte ← b(ende + start)/2c
if M[mitte] = key then
return mitte
else if M[mitte] < key then
return BinSearch(M, mitte + 1, ende, key )
else
return BinSearch(M, start, mitte − 1, key )
end if
else
return -1
end if
end function
Informatik 2
Suchen und Bäume
Einleitung
Binäre Suche Code 1 - C++
1
2
3
4
5
6
7
8
9
10
11
12
13
int binSearch (int field[], int start, int end, int key)
if (end >= start){
int mid = (end + start) / 2;
if (field[mid] == key){
return mid;
} else if (field[mid] < key){
return binSearch (field, mid+1, end, key);
} else {
return binSearch (field, start, mid-1, key);
}
} else {
return -1;
}}
Informatik 2
Suchen und Bäume
Einleitung
Binäre Suche Code 2
function B IN S EARCH(M, start, ende, key )
if ende > start then
mitte ← b(ende + start)/2c
if M[mitte] < key then
return BinSearch(M, mitte + 1, ende, key )
else
return BinSearch(M, start, mitte, key )
end if
else
if M[start] = key then
return start
else
return -1
end if
end if
end function
Informatik 2
Suchen und Bäume
Einleitung
Interpolationssuche
I
Es geht noch besser, wenn man mit den Elementen
rechnen kann und die Werte ungefähr gleichverteilt sind
· (k − s))
(also a[k ] ≈ M[s] + M[e]−M[s]
e−s
I
Statt der Mitte wird nun der ungefähre Index durch lineare
Interpolation berechnet:
m=
I
e−s
· (W − M[s]) + s
M[e] − M[s]
Falls die Annahmen stimmen liegt die Laufzeit in
O(log log n), wenn nicht ist es langsamer als Binäre Suche
Informatik 2
Suchen und Bäume
Einleitung
Interpolationssuche Code
function I NTERPOL S EARCH(M, start, ende, key )
if ende > start
j then
k
−M[start])
mitte ← start + (ende−start)·(key
M[ende]−M[start]
if (mitte < start) ∨ (mitte > ende) then
return -1
else if M[mitte] = key then
return mitte
else if M[mitte] < key then
return InterpolSearch(M, mitte + 1, ende, key )
else
return InterpolSearch(M, start, mitte − 1, key )
end if
else
if M[ende] = key then
return start
else
return -1
end if
Informatik 2
Suchen und Bäume
Einleitung
Interpolationssuche - C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
int interpolSearch(int field[],int start,int end,int key){
if (end > start){
int mid = (end - start) * (key - field[start]);
mid /= (field[end] - field[start]);
mid += start;
if ((mid < start) || (mid > end)) {
return -1;
} else if (field[mid] == key) {
return mid;
} else if (field[mid] < key) {
return interpolSearch (field, mid+1, end, key);
} else {
return interpolSearch (field, start, mid-1, key);
}
} else { // end == start
if (field[end] == key) {
return start;
} else {
return -1;
}}}
Informatik 2
Suchen und Bäume
Einleitung
I
Auf sortierten Arrays ist alles fein, allerdings ist der lineare
Aufwand zum Einfügen und Löschen von Elementen zu
hoch, wenn dies häufig (im Vergleich zum Suchen)
vorkommt.
I
Listen sind da besser, aber da geht die Suche per
Intervallschachtelung nicht, weil der direkte wahlfreie
Zugriff auf ein Element fehlt
I
Suchbäume sollen dabei helfen. Nicht nur die Suche auch
das Einfügen und Löschen soll damit in O(log n) ablaufen
Informatik 2
Suchen und Bäume
Einleitung
Nicht diese Bäume!
Informatik 2
Suchen und Bäume
Einleitung
Definition
Ein Baum ist eine Menge von Knoten und Kanten, bei denen es
zwischen zwei Knoten genau einen Weg gibt.
42
14
33 -3
13
9
52
54
Eine Menge von Bäumen wird Wald genannt.
Informatik 2
Suchen und Bäume
Einleitung
Bezeichnungen
42
14
33 -3
I
I
I
I
I
I
I
13
9
52
54
42 ist der Wurzelknoten
33, -3, 9 sind Kindknoten von 14
14 ist der Elternknoten zu 33, -3, 9
14, 13, 52 sind Geschwisterknoten
33, -3, 9, 54, 52 sind Blätter (haben keine Kindknoten)
13, 54 bilden einen Unterbaum (mit Wurzel 13) zu Knoten
42
Die Höhe des Baumes ist die Anzahl der “Generationen”
(hier 3)
Informatik 2
Suchen und Bäume
Suchen in Bäumen
Suchen in Bäumen
Suchen kann man mittels
I
Tiefensuche (Depth-First Search):
Gehe solange in die Tiefe bis es nicht mehr weitergeht,
gehe dann zum nächsten noch nicht besuchten Nachbarn
und wiederhole das Ganze.
I
Breitensuche (Breadth-First-Search):
Besuche nacheinander alle Knoten eines Levels (einer
Generation), bevor der nächste Level angegangen wird.
Kann man dadurch implementieren, daß die Knoten eines
Levels in einer Liste zwischengespeichert werden, die
dann abgearbeitet wird. Beim Abarbeiten dieser Liste
schreibt dann jeder Knoten seine Kinder wieder in die
Liste. Ist z.B praktisch für die Visualisierung.
Informatik 2
Suchen und Bäume
Diverse Beispiele
Man trifft häufig auf baumartige Strukturen, so lässt sich der
Ausdruck
(5.4 − 3.5) + 3.1
als Baum ausdrücken:
+
5.4
3.1
3.5
Informatik 2
Suchen und Bäume
Diverse Beispiele
Entscheidungsbaum
Auch bei Entscheidungsabläufen sind Bäume beliebt. Hier ein
einfacher Entscheidungsbaum zur Vorhersage, ob ein
Apfelbaum Früchte tragen wird aus Wikipedia:
Informatik 2
Suchen und Bäume
Diverse Beispiele
Spielbaum
Spielbäume mit allen möglichen Zügen:
Informatik 2
Suchen und Bäume
XML
XML
I
XML = Extensible Markup Language
I
zur Darstellung hierarchisch strukturierter Daten in
Textform
I
wird u.a. für den plattform- und
implementationsunabhängigen Austausch von Daten
zwischen Computersystemen eingesetzt
I
Als Baumknoten gibt es u.a. Elemente, die durch Startund End-Tags ausgezeichnet werden, sowie auch
einfachen Text.
I
Hierarchie entsteht durch ineinander verschachtelte Tags
Informatik 2
Suchen und Bäume
XML
Beispiel 1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?xml version="1.0" encoding="UTF-8"?>
<verzeichnis>
<titel>Staedteverzeichnis</titel>
<eintrag>
<stichwort>Genf</stichwort>
<text>
Genf ist der Sitz von ...
</text>
</eintrag>
<eintrag>
<stichwort>Stuttgart</stichwort>
<text>
Stuttgart ist eine Stadt, die ...
</text>
</eintrag>
</verzeichnis>
Informatik 2
Suchen und Bäume
XML
Beispiel 2
Die XML Version des Baums für den arithmetischen Ausdruck
von Folie 27:
1
2
3
4
5
6
7
<ADD>
<SUB>
<value> 5.4 </value>
<value> 3.5 </value>
</SUB>
<value> 3.1 </value>
</ADD>
Informatik 2
Suchen und Bäume
Binärbäume
Datenstruktur Binärbäume
Am häufigsten hat man es mit Bäumen zu tun, bei denen jeder
Knoten höchstens zwei Kinder hat, den sogenannten
Binärbäumen.
1
typedef int DATATYPE;
2
3
4
5
6
7
8
struct Node
{
DATATYPE data;
Node* left;
Node* right;
};
9
10
typedef Node* BinaryTree;
Allgemein steht da eine Liste von Kindern drin.
Informatik 2
Suchen und Bäume
Binärbäume
Grundlegende Fkt. zu Binärbäumen
1
2
3
void initialize (BinaryTree& bt){
bt = NULL;
}
4
5
6
7
bool isEmpty (BinaryTree& bt){
return (bt == NULL);
}
8
9
10
11
12
13
14
15
bool isLeaf (BinaryTree& bt){
if (isEmpty (bt)){
return false;
} else {
return (isEmpty(bt->left) && (isEmpty (bt->right)));
}
}
Informatik 2
Suchen und Bäume
Binärbäume
Als Array gespeichert
1
2
3
4
5
6
7
#define MAX_HOEHE 5
#define MAX_KNOTEN ((1 << MAX_HOEHE)-1)
typedef int DATATYPE;
struct Node {
DATATYPE data;
};
typedef Node[MAX_KNOTEN] BinaryTree;
I
Die einzelnen Levels werden nacheinander im Array
gespeichert
I
Die Wurzel hat den Index 0
I
Die Wurzel des linken Unterbaumes zu Knoten i ist 2i + 1,
die des rechten Unterbaumes 2i + 2
Informatik 2
Suchen und Bäume
Binärbäume
Traversierungen
Es gibt es 3 unterschiedliche Strategien zur Tiefensuche:
I Preorder:
I
I
I
I
Inorder:
I
I
I
I
Knoten bearbeiten / überprüfen
linken Unterbaum betrachten
rechten Unterbaum betrachten
linken Unterbaum betrachten
Knoten bearbeiten / überprüfen
rechten Unterbaum betrachten
Postorder:
I
I
I
linken Unterbaum betrachten
rechten Unterbaum betrachten
Knoten bearbeiten / überprüfen
Informatik 2
Suchen und Bäume
Binärbäume
Beispielimplementierung Preorder
1
2
3
4
5
6
7
8
9
void printPreorder (BinaryTree bt)
{
if (!isEmpty(bt))
{
std::cout << bt->data << " ";
printPreorder (bt->left);
printPreorder (bt->right);
}
}
Informatik 2
Binäre Suchbäume
Einleitung
Binäre Suchbäume
Ein binärer Suchbaum ist ein binärer Baum für den gilt:
I
Für jeden Knoten sind die Werte im linken Unterbaum
kleiner als der Eigene
I
Für jeden Knoten sind die Werte im rechten Unterbaum
größer als der Eigene
Bemerkungen:
I
Die Höhe ist im besten Fall (und im Durchschnitt)
O(log(n)) bei n Knoten.
I
Die Suche nach einem Wert kann hier wie bei der binären
Suche wohl durchschnittlich in O(log(n)) erfolgen.
I
Allerdings lassen sich hier Werte effizienter einfügen und
wieder entfernen.
Informatik 2
Binäre Suchbäume
Einleitung
Suchen
1
2
3
4
5
6
7
8
9
10
11
12
bool search (BinaryTree bt, DATATYPE d){
if (isEmpty (bt)) {
return false;
}
else {
if (bt->data == d){
return true;
} else if (d < bt->data) {
return search (bt->left, d);
} else {
return search (bt->right, d);
}}}
Informatik 2
Binäre Suchbäume
Einfügen von Knoten
Einfügen von Knoten
Einfacher Algorithmus zum Einfügen eines neuen Knotens:
I
Man geht so lange den Baum herunter bis man an der
richtigen Position ist
I
Ist der neue Knoten kleiner als (oder hat den gleichen Wert
wie) der aktuelle Wurzelknoten, dann füge ihn (rekursiv) in
den linken Unterbaum ein
I
Ist der neue Knoten größer als der aktuelle Wurzelknoten,
dann füge ihn (rekursiv) in den rechten Unterbaum ein
I
Am Ende, wenn der aktuelle Unterbaum leer ist, wird dann
der Knoten tatsächlich eingefügt (als Blatt)
Die Anzahl der Schritte entspricht im dümmsten Fall der Höhe
des Baumes. Im durchschnittlichen Fall liegt das in O(log(n)) !
Informatik 2
Binäre Suchbäume
Einfügen von Knoten
Einfügen in Binärbäume
1
2
3
4
5
6
7
8
9
10
11
12
13
void addElement (BinaryTree& bt, DATATYPE d){
if (isEmpty (bt)) {
Node* add = new Node;
add->data = d;
add->left = NULL;
add->right = NULL;
bt = add;
} else {
if (d < bt->data){
addElement (bt->left, d);
} else if (d > bt->data){
addElement (bt->right, d);
}}}
Informatik 2
Binäre Suchbäume
Entfernen von Knoten
Entfernen von Knoten
Schon etwas komplizierter:
I Ist der zu löschende Knoten ein Blatt, kann er einfach
gelöscht werden.
I Hat er ein Kind, dann wird er nach dem Löschen einfach
durch dessen Teilbaum ersetzt.
I Mit 2 Kindern: Der linke Teilbaum wird an die Position des
gelöschten Knotens gesetzt und der rechte Teilbaum wird
an diesen dann passend angehängt (geht auch
symmetrisch).
I Besser mit 2 Kindern: Ersetze den gelöschten Knoten
durch den nächstgrößeren Knoten (im rechten Teilbaum
immer nach links). Dessen evtl. rechter Unterbaum ersetzt
ihn dann.
Auch hier ist der Aufwand von der Höhe abhängig, also
durchschnittlich in O(log(n)) !
Informatik 2
Binäre Suchbäume
Entfernen von Knoten
Löschen in Binärbäumen Basis
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
void deleteElement (BinaryTree& bt, DATATYPE d){
if (isEmpty (bt)){
return;
} else if (d < bt->data){
deleteElement (bt->left, d);
} else if (d > bt->data){
deleteElement (bt->right, d);
} else {
if (isLeaf (bt)){
delete bt;
bt = NULL;
} else if (bt->left == NULL) {
Node* old = bt;
bt = bt->right;
delete old;
} else if (bt->right == NULL){
Node* old = bt;
bt = bt->left;
delete old;
} else {
...
Informatik 2
Binäre Suchbäume
Entfernen von Knoten
Löschen in Binärbäumen 1.Variante
} else {
Node* tempRight = bt->right;
Node* old = bt;
bt = bt->left;
Node* it = bt;
while (it->right != NULL) {
it = it->right;
}
it->right = tempRight;
delete old;
1
2
3
4
5
6
7
8
9
10
11
}}}
Informatik 2
Binäre Suchbäume
Entfernen von Knoten
Loeschen in Binaerbaeumen 2.Variante
} else {
Node* it = bt->right;
Node* itParent = bt;
while (it->left != NULL){
itParent = it;
it = it->left;
}
bt->data = it->data;
Node* old = it;
itParent->left = it->right;
delete old;
1
2
3
4
5
6
7
8
9
10
11
12
}}}
Informatik 2
Binäre Suchbäume
Entfernen von Knoten
Suchpfadlänge
I Die Suchpfadlänge für einen einzelnen Knoten ist die Zahl an
besuchten Knoten bis er gefunden wurde. Das ist der Abstand des
Knotens zur Wurzel+1 (die Wurzel selbst)
I Die innere Suchpfadlänge S(T) eines Baumes T ist die Summe der
Suchpfadlängen aller Knoten
I Für einen Baum T mit n Knoten und linkem Teilbaum Tl und rechtem
Teilbaum Tr gilt dabei:
S(T ) = S(Tl ) + S(Tr ) + n
I Die durchschnittliche Suchpfadlänge eines Baumes mit n Knoten ist
1
S(T )
n
I Nun interessiert die erwartete innere Suchpfadlänge. D.h. welche
innere Suchpfadlänge kann man bei einen zufällig erzeugten Baum
erwarten ?
I Ohne Beweis: Das ist ca. ES(n) = 1.386n log2 n − 0.846n + O(log n)
I Damit ist die zu erwartende durchschn. Suchpfadlänge:
ES(n)
∈ O(log n)
n
Informatik 2
AVL-Bäume
Motivation
Motivation
I
Durch Löschen oder "ungeschicktes" Einfügen (z.B.
1,2,3,4,5,6), kann der Baum entarten, d.h. es entstehen
lange Äste.
I
In diesen Fällen liegt dann der Aufwand zum Suchen,
Einfügen und Löschen bei jeweils O(n).
I
Ergo muss eine solche Entartung verhindert werden durch
zusätzliche Operationen, die aber nicht die Suchbaum
Eigenschaft verletzen und in O(1) ablaufen.
I
Dabei helfen “Rotationen”
Informatik 2
AVL-Bäume
Rotationen
Rotation rechts
D
B
B
D
E
A
C
A
C
E
Informatik 2
AVL-Bäume
Rotationen
Rotation rechts
1
2
3
4
5
6
7
void rotationRight (Tree& subtree)
{
Node* temp = subtree->left;
subtree->left = (subtree->left)->right;
temp->right = subtree;
subtree = temp;
}
Informatik 2
AVL-Bäume
Rotationen
Rotation links
B
D
D
B
A
E
C
E
A
C
Informatik 2
AVL-Bäume
Rotationen
Rotation links
1
2
3
4
5
6
7
void rotationLeft (Tree& subtree)
{
Node * temp = subtree->right;
subtree->right = (subtree->right)->left;
temp->left = subtree;
subtree = temp;
}
Informatik 2
AVL-Bäume
AVL-Bäume
AVL-Bäume
I
Name kommt von den Entdeckern Adelson-Velski und
Landis (1962)
I
Jeder Knoten erhält ein zusätzliches Attribut: die Balance
I
Balance eines Knotens = Höhe des rechten Unterbaums Höhe des linken Unterbaums
I
Erlaubt sind nur Balancen von -1, 0, +1 für die Knoten des
Baumes
I
Balancen von -2 oder +2 können durch Rotationen
behoben werden.
Informatik 2
AVL-Bäume
AVL-Bäume
Datenstruktur
1
typedef int DATATYPE;
2
3
4
5
6
7
8
9
struct Node
{
DATATYPE data;
int balance;
Node* left;
Node* right;
};
10
11
typedef Node* AVLTree;
Informatik 2
AVL-Bäume
Einfügen
Einfügen
I
Das Einfügen funktioniert vorerst wie beim normalen
Suchbaum.
I
Das einfügte Element hat die Balance 0.
Für die direkten Vorgänger ändert sich die Balance
I
I
I
I
um +1, wenn der neue Knoten im rechten Unterbaum
um -1, wenn der neue Knoten im linken Unterbaum
eingefügt wurde
Wurde die Balance zu:
I
I
I
0: Höhe blieb gleich. Die Balance der darüberliegenden
Knoten muß nicht mehr angepasst werden.
-1 oder 1: Die Höhe des Unterbaums hat sich um 1 erhöht.
Die Balance muß auch im direkten Vorgänger angepasst
werden.
-2 oder +2: Es muss per Rotationen repariert werden.
Informatik 2
AVL-Bäume
Einfügen
Einfügen Reparatur I
Balance der Wurzel (D) ist -2, das linke Kind (B) hat eine
Balance von -1: Rechtsrotation um D
D
B
B
D
E
C
A
A
C
E
Informatik 2
AVL-Bäume
Einfügen
Einfügen Reparatur II
Balance der Wurzel (F) ist -2, das linke Kind (B) hat eine
Balance von +1: Linksrotation um B, dann Rechtsrotation um F.
(Es kann auch E um eins höher als C sein, aber das ändert
nicht viel)
F
F
B
D
D
A
B
B
G
E
A
C
F
G
E
A
E
C
D
C
G
Informatik 2
AVL-Bäume
Einfügen
Einfügen Reparatur III/IV
Das Vorgehen bei einer Balance von +2 für den aktuell
bearbeiteten Knoten ist gespiegelt zu den vorherigen Fällen:
I
Rechtes Kind hat Balance +1: Linksrotation um aktuellen
Knoten
I
Rechtes Kind hat Balance -1: Rechtsrotation um das
rechte Kind und dann Linksrotation um den aktuellen
Knoten.
In jedem der Fälle hat dann der aktuelle Knoten hinterher die
Balance 0, und die Balance der darüberliegenden Knoten
ändert sich somit nicht mehr.
Informatik 2
AVL-Bäume
Löschen
Löschen
I
Löschen funktioniert bis auf das Balancieren wie beim
Suchbaum.
I
Auch hier müssen auf dem Pfad der direkten Vorgänger
die Balancen angepasst werden.
Wurde die Balance zu:
I
I
I
I
-1 oder 1: (vorher 0) Höhe blieb gleich, nur ein Unterbaum
ist kürzer geworden. Die Balance der darüberliegenden
Knoten muß nicht mehr angepasst werden.
0: Der (ehemals) höhere Unterbaum wurde gekürzt. Damit
wurde die Höhe des aktuellen Baums um 1 reduziert! Die
Balance muß auch im direkten Vorgänger angepasst
-2 oder +2: Es muss per Rotationen repariert werden.
Informatik 2
AVL-Bäume
Löschen
Löschen Reparatur I/II/IV/V
Folgende Fälle sind mit den gleichen Rotationen zu behandeln
wie im Falle des Einfügens:
I
Balance der Wurzel ist -2, Balance des linken Kindes ist -1
⇒ Rechtsrotation um Wurzel.
I
Balance der Wurzel ist -2, Balance des linken Kindes ist +1
⇒ Linksrotation um linkes Kind, Rechtsrotation um Wurzel.
I
Balance der Wurzel ist 2, Balance des rechten Kindes ist
+1 ⇒ Linksrotation um Wurzel.
I
Balance der Wurzel ist 2, Balance des linken Kindes ist -1
⇒ Rechtsrotation um rechtes Kind, Linksrotation um
Wurzel.
Allerdings muß nun der direkte Vorgänger angepasst werden !
Informatik 2
AVL-Bäume
Löschen
Löschen Reparatur III/VI
Balance der Wurzel (D) ist -2, Balance des linken Kindes (B) ist
0: Rechtsrotation um Wurzel (symmetrisch für +2)
D
B
B
D
E
A
A
C
E
C
Höhe hat sich nicht geändert: Die direkten Vorgänger müssen
nicht mehr angepasst werden.
Informatik 2
Einfache Sortieralgorithmen
Allgemeines
I
Die Aufgabe von Sortieralgorithmen ist es eine Folge
a0 , a1 , . . . , an−1 von Elementen anhand einer Ordnungsrelation
zu sortieren.
I
Für die Implementierungen hier soll die Ordnungsrelation ≤ auf
den ganzen Zahlen verwendet werden. (d.h. die Zahlen sollen
aufsteigend sortiert werden).
I
Also ist die Permutation π gesucht mit aπ(i) ≤ aπ(i+1) für
i ∈ {0, . . . , n − 2}
I
Für die Aufwandsabschätzungen sollen die Vergleiche von
Folgenelementen abgeschätzt werden.
I
Als Maß für die Unsortiertheit eignet sich die Inversionszahl
(oder Fehlstand):
I(A) = |{(i, j) | i < j und ai > aj }|
. 0 (sortiert) ≤ I(A) ≤
n(n−1)
2
(falsch herum sortiert)
Informatik 2
Einfache Sortieralgorithmen
Ein ganz einfacher Sortieralgorithmus: Bogosort
Gegeben sei eine Folge von Zahlen.
1. Überprüfe ob die Folge sortiert ist.
2. Wenn ja, dann höre auf und vermelde Erfolg
3. Wenn nein, mische die Folge zufällig durch und gehe zu
Schritt 1
Aufwandsabschätzungen für diesen Algorithmus:
I
Best case: O(n)
I
Average case: O(n · n!)
I
Worst case: Algorithmus bricht nicht ab
Da wird es wohl bessere Algorithmen geben...
Informatik 2
Einfache Sortieralgorithmen
Selection Sort
Selection Sort
I
Das Feld sei in einen sortierten (anfangs leer) und
unsortierten Bereich aufgeteilt. Am Anfang gehört alles
zum unsortierten Bereich.
I
Finde das Minimum aus dem unsortierten Bereich
I
Vertausche es mit dem ersten Element des unsortierten
Bereichs
Informatik 2
Einfache Sortieralgorithmen
Selection Sort
for i ← 0 . . . n − 1 do
minIndex ← Minimum von ai , . . . , an−1
if i 6= minIndex then
Vertausche aminIndex mit ai
end if
end for
Informatik 2
Einfache Sortieralgorithmen
Selection Sort
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
typedef int Datatype;
void swap (Datatype* a, Datatype* b){
Datatype helper = *a;
*a = *b;
*b = helper;
}
void selectionSort (Datatype field[], int fieldSize){
int minIndex;
for (int i = 0; i < fieldSize - 1; i++){
minIndex = i;
for (int j = i+1; j < fieldSize; j++){
if (field[j] < field[minIndex]){
minIndex = j;
}
}
if (i != minIndex){
swap (&(field[i]), &(field[minIndex]));
}}}
Informatik 2
Einfache Sortieralgorithmen
Selection Sort
Aufwandsabschätzung Selection Sort
I
I
I
Best Case: O(n2 )
Die Folge ist schon sortiert:
P
n·(n−1)
Vergleiche: n−1
≈ 12 n2 ,
i=1 i =
2
Tauschaktionen: 0
Worst Case: O(n2 )
Die Folge ist verkehrtrum sortiert:
≈ 21 n2 ,
Vergleiche: n·(n−1)
2
Tauschaktionen: n − 1 ≈ n
Average Case: O(n2 )
Vergleiche: n·(n−1)
≈ 21 n2 ,
2
Tauschaktionen: ≤ n − 1 ≈ n
Informatik 2
Einfache Sortieralgorithmen
Bubble Sort
Bubble Sort
I Es werden zwei benachbarte
Elemente a[i] und a[i+1] verglichen.
Falls a[i] > a[i+1] ist, werden die
beiden vertauscht.
I Danach wird das nächste Paar
miteinander verglichen.
I Ist man am Ende angelangt, beginnt
man wieder am Anfang.
I Das macht man solange bis es in
einem Durchlauf keine
Vertauschungen mehr gibt.
I Die großen Elemente “blubbern”
dabei nach oben
Figure : Visualisierung von
Bubblesort. Aus engl.
Wikipedia
Informatik 2
Einfache Sortieralgorithmen
Bubble Sort
Vertauschungen ← 1
max ← n − 1
while Vertauschungen > 0 do
Vertauschungen ← 0
for i ← 0, . . . , max − 1 do
if ai > ai+1 then
Vertausche ai mit ai+1
Vertauschungen ← Vertauschungen + 1
max ← i
end if
end for
end while
Informatik 2
Einfache Sortieralgorithmen
Bubble Sort
1
2
3
4
5
6
7
8
9
10
11
12
13
14
void bubbleSort (Datatype field[], int size){
int swaps = 1;
int max = size-1;
while (swaps > 0){
swaps = 0;
for (int i = 0; i < max; i++){
if (field[i] > field[i+1]){
swap (&(field[i]), &(field[i+1]));
swaps++;
max = i;
}
}
}
}
Informatik 2
Einfache Sortieralgorithmen
Bubble Sort
Aufwandsabschätzung Bubblesort
Beim Average Case wird eine Gleichverteilung der Elemente
angenommen (jede Permutation ist gleich wahrscheinlich).
I Best Case: O(n)
Die Folge ist schon sortiert:
Vergleiche: n-1, Tauschaktionen: 0
I Worst Case: O(n2 )
Die Folge ist verkehrtrum sortiert:
Pn−1
Vergleiche: i=1
i = n·(n−1)
≈ 12 n2 ,
2
Tauschaktionen: n·(n−1)
≈ 12 n2
2
I Average Case: O(n2 )
Vergleiche: ≈ 21 n2 ,
Tauschaktionen: ≈ 14 n2
Leider auch bei kleinem Fehlstand quadratischer Aufwand (z.B.
2 3 4 5 6 1).
Informatik 2
Einfache Sortieralgorithmen
Insertion Sort
Insertion Sort
I
Das Feld sei in einen sortierten und unsortierten Teil aufgeteilt.
I
Am Anfang ist natürlich nur das erste Element im sortierten Teil
und der ganze Rest bildet den unsortierten Teil
I
In jedem Schritt wird das erste Element aus dem unsortierten
Teil genommen und an die richtige Stelle im sortierten Teil
eingefügt. Dazu wird es mit dem linken Nachbarn verglichen und
gg.falls vertauscht bis der linke Nachbar kleiner ist.
Informatik 2
Einfache Sortieralgorithmen
Insertion Sort
Pseudocode 1
for i ← 1 . . . n − 1 do
pos ← i − 1
while apos > apos+1 und pos ≥ 1 do
Vertausche apos mit apos+1
pos ← pos − 1
end while
end for
Informatik 2
Einfache Sortieralgorithmen
Insertion Sort
Pseudocode 2
for i ← 1 . . . n − 1 do
wert ← ai
pos ← i − 1
while apos > wert und pos ≥ 1 do
apos+1 ← apos
pos ← pos − 1
end while
apos+1 ← wert
end for
Informatik 2
Einfache Sortieralgorithmen
Insertion Sort
1
2
3
4
5
6
7
8
9
10
11
12
13
void insertionSort (Datatype field[], int fieldSize){
int value;
int testPos;
for (int i = 1; i < fieldSize; i++){
value = field[i];
testPos = i - 1;
while ((testPos >= 0) && (field[testPos] > value)) {
field[testPos + 1] = field[testPos];
testPos--;
}
field[testPos + 1] = value;
}
}
Informatik 2
Einfache Sortieralgorithmen
Insertion Sort
Aufwandsabschätzung Insertion Sort
I
I
I
Best Case: O(n)
Die Folge ist schon sortiert:
Vergleiche: n-1, Tauschaktionen: 0
Worst Case: O(n2 )
Die Folge ist verkehrtrum sortiert:
P
n·(n−1)
≈ 12 n2 ,
Vergleiche: n−1
i=1 i =
2
P
n2 +n−2
Tauschaktionen: n−1
≈ 12 n2
i=1 i + n − 1 =
2
Average Case: O(n2 )
Vergleiche: ≈ 14 n2 , Tauschaktionen: ≈ 41 n2
Aber: Bei schon fast sortierten Folgen (geringer
Fehlstand) ist das Verfahren recht flott (in jedem Schritt
wird der Fehlstand um 1 verringert, also O(n + I(A)).
Informatik 2
Effiziente Sortieralgorithmen
Divide et Impera!
I
Oder auch Divide & Conquer
I
Ursprünglich Militärstrategie (schon Sunzi sprach davon
500 v.Chr.)
I
Zerteile ein Problem in kleinere und löse dann diese
I
Sehr schön mit Rekursion zu machen : In einer Funktion
wird das Problem zerlegt und die gleiche Funktion mit den
kleineren Teilen wieder aufgerufen. Danach muß evtl.
noch zusammengefügt werden.
I
Quicksort und Mergesort bedienen sich dieser Strategie.
I
Natürlich auch viele andere Algorithmen
Informatik 2
Effiziente Sortieralgorithmen
Quicksort
Quicksort Übersicht
1. Suche ein Element aus der Folge aus (Pivotelement).
2. Ordne die Liste so um , daß alle Elemente, die kleiner als
das Pivotelement sind, links von diesem und alle
Elemente, die größer sind, rechts von diesem stehen.
3. Sortiere nun getrennt die Elemente links vom Pivotelement
und die Elemente rechts davon (natürlich mit Quicksort)
4. Eine Folge mit 0 oder 1 Element ist von sich aus sortiert.
Informatik 2
Effiziente Sortieralgorithmen
Quicksort
Partitionieren des Feldes
Das mit dem Partitionieren hört sich einfacher an, als es
wirklich ist.
Die einfachste Lösung ist:
I
allokiere zwei zusätzliche Felder leftField und rightField
I
gehe über das sortierende Feld und füge die Elemente in
leftField oder rightField ein.
I
sortiere leftField und rightField mit Quicksort
I
kopiere die beiden in das ursprüngliche Feld
Der zusätzliche Speicheraufwand ist natürlich nicht gut...
Informatik 2
Effiziente Sortieralgorithmen
Quicksort
Partitionieren des Feldes 2
Als Pivotelement wähle das Element in der Mitte.
I Initialisiere zwei Iteratoren leftIt, rightIt mit den
Indices des linken Randes bzw. des rechten Randes-1
I Solange nun leftIt <= rightIt
I
I
I
I
I
Suche von links das nächste Element welches größer oder
gleich dem Pivotelement ist, so daß leftIt danach auf
dieses zeigt.
Suche von rechts das nächste Element welches kleiner
oder gleich dem Pivotelement ist, so daß rightIt danach
auf dieses zeigt.
Vertausche die beiden und erhöhe leftIt um 1 und
erniedrige rightIt um 1.
Am Ende ist leftIt rechts von rightIt (zwischendrin
ist höchstens das Pivotelement).
Mache rekursiv mit dem Teil bis rightIt und dem Teil ab
leftIt weiter.
Informatik 2
Effiziente Sortieralgorithmen
Quicksort
Aufwandsabschätzung Quicksort
I
I
I
Best Case: O(n log(n))
Die Pivotelemente sind jeweils das mittlere Element
der sortierten Teilliste
Average Case: O(n log(n))
Lässt sich herleiten!
Worst Case: O(n2 )
Die Pivotelemente sind immer an einem Rand der
sortierten Teilliste.
Ist dann wie Selection Sort !
Informatik 2
Effiziente Sortieralgorithmen
Quicksort
Bemerkungen
I
Um den Worst Case zu verhindern als Pivotelement häufig
der Median aus dem Element am linken Rand, in der Mitte
und am rechten Rand verwendet
I
In der Praxis spielt der Worst Case dann eigentlich keine
Rolle und Quicksort ist auch wirklich “quick”.
Informatik 2
Effiziente Sortieralgorithmen
Mergesort
Mergesort
1. Wenn die Folge 0 oder 1 Elemente enthält, ist sie
schon sortiert.
2. Teile die unsortierte Folge in 2 ungefähr gleich große
Teillisten. (Hier wird extra Speicher benötigt !)
3. Sortiere diese Teilfolgen rekursiv mit Mergesort.
4. Verschmelze (Merge) die dann sortierten Teilfolgen
zu einer einzelnen sortierten Liste.
Informatik 2
Effiziente Sortieralgorithmen
Mergesort
Mergesort
Informatik 2
Effiziente Sortieralgorithmen
Mergesort
Der merge Schritt
Der merge Schritt vereint zwei jeweils sortierte Folgen zu einer
einzelnen sortierten Folgen
I
Vergleiche die aktuellen Elemente der beiden Folgen
I
Das kleinere füge an die aktuelle Position der
Ergebnisfolge an
I
Schalte zum nächsten Element der “Gewinner”folge und
Ergebnisfolge weiter
I
Wurde eine der Eingabefolgen vollkommen eingefügt, fülle
die Ergebnisfolge mit dem Rest der anderen Folge auf.
Informatik 2
Effiziente Sortieralgorithmen
Mergesort
Aufwandsabschätzung Mergesort
I
I
I
Best Case: O(n log(n))
Average Case: O(n log(n))
Worst Case: O(n log(n))
Informatik 2
Effiziente Sortieralgorithmen
Bucketsort
Bucketsort
Hier ist der Trick, daß nicht in place sortiert wird, sondern
zusätzliche “buckets” verwendet werden (häufig genauso viele
wie es Elemente in der Folge gibt):
I
Erstelle ein Array von “buckets” (als Listen implementiert),
wobei jeder für einen eigenen Bereich zuständig ist.
I
Füge jedes Element der Liste in den dafür passenden
“bucket”
I
Sortiere jeden “bucket” (z.B. Insertion sort such schon
während dem vorherigen Schritt)
I
Füge die “buckets” in der richtigen Reihenfolge zusammen
Informatik 2
Effiziente Sortieralgorithmen
Bucketsort
Aufwandsabschätzung Bucketsort
Bei n Elementen und n “buckets”
I
Best Case: O(n)
Jeweils ein Element pro “bucket”
I
Average Case: O(n)
I
Worst Case: O(n2 ) (bei Insertion Sort)
Alles geht in einen Bucket.
Informatik 2
Effiziente Sortieralgorithmen
Heaps
Heaps
I
Heaps sind eine abstrakte Datenstruktur, die Mengen
speichert bei denen jedes Element einen Schlüssel hat,
der die Priorität festlegt (Das Element kann auch selbst der
Schlüssel sein)
I
Wird zumeist mit Bäumen realisiert.
I
Es muß die Heapbedingung erfüllt sein: Der Schlüsselwert
aller Kinder muß kleiner (bzw. größer) sein, als der
Schlüsselwert des Elternknotens (je nachdem ob oben das
Maximum oder Minimum stehen soll, es also ein
Max-Heap oder ein Min-Heap wird).
I
Eignet sich hervorragend für Prioritätswarteschlangen.
Informatik 2
Effiziente Sortieralgorithmen
Heaps
Übliche Operationen
Für einen Min-Heap (mit minimalem Schlüssel als Wurzel)
I
create: Erstelle einen leeren Heap.
I
insert: Füge einen neuen Knoten in den Heap hinzu, so
daß die Heapbedingung erfüllt ist
I
find-min: Liefere das minimale Element zurück
I
delete-min: Entferne das Element mit dem geringsten
Schlüssel
I
decrease-key: Reduziere den Wert des Schlüssels eines
Knotens
Informatik 2
Effiziente Sortieralgorithmen
Heaps
Beispiel eines Heaps als Binärbaum
Quelle: Wikipedia
Informatik 2
Effiziente Sortieralgorithmen
Heaps
Implementation 1
I
Binärbäume lassen sich auch hervorragend als Array
darstellen, wenn eine maximale Anzahl vorher bekannt ist.
I
Die Kinder eines Knotens an Position i stehen dann an
Position 2i + 1 und 2i + 2 (Wurzel ist an Position 0).
I
Umgekehrt ist der Elternknoten
eines Kindes an Position i
dann an Position i−1
2
Informatik 2
Effiziente Sortieralgorithmen
Heaps
Implementation 2
insert:
I
das einzufügende Element wird im Array ans Ende
hinzugefügt (im Baum ist es dann der rechte Nachbar vom
rechtesten Knoten auf dem untersten Level)
I
Nun müssen die Heapbedingungen überprüft werden:
Vergleiche das soeben eingefügte Element mit seinem
Elternknoten. Ist der Schlüssel dort kleiner, ist alles in
Ordnung. Wenn nicht, dann werden die beiden vertauscht
und es geht weiter (einen Level höher) bis entweder die
Heapbedingung erfüllt wurde oder die Wurzel erreicht
wurde (Bubble up).
I
Durch diese Methode entartet der Baum nicht, so daß die
Komplexität der Operation O(log n) ist.
Informatik 2
Effiziente Sortieralgorithmen
Heaps
Implementation 3
delete-min:
I
Ersetze die Wurzel (an der sich das Minimum befindet) mit
dem letzten Element
I
Nun müssen die Heapbedingungen überprüft werden:
Vergleiche das soeben eingefügte Element mit seinen
beiden Kindern. Sind die Schlüssel der beiden größer, ist
alles in Ordnung. Wenn nicht, dann vertausche den
Knoten mit dem Kind, das den kleinsten Schlüssel und es
geht weiter (einen Level tiefer) bis entweder die
Heapbedingung erfüllt wurde oder die Blattebene erreicht
wurde (Bubble down).
I
Durch diese Methode entartet der Baum nicht, so daß die
Komplexität der Operation O(log n) ist.
Informatik 2
Effiziente Sortieralgorithmen
Heaps
Implementation 4
decrease-key:
I
Finde den Knoten zu dem passenden Element.
I
Verringere den Schlüsselwert im Knoten
I
Evtl. sind die Heapbedingungen verletzt. Das Vorgehen ist
das gleiche wie beim Einfügen (Bubble up).
I
Die Komplexität ist abhängig von der Knotensuche:
O(Suche + log n).
I
Um zu einem Element schnell den Index zu finden, sollte
man noch eine zusätzliche Datenstruktur hinzunehmen,
die einem Element den Index im Array zuweisen. Ist der zu
speichernde Wert ein eindeutiger Integer, so geht das
ganz einfach mit einem zusätzlichen Array.
Informatik 2
Effiziente Sortieralgorithmen
Heaps
Christmas tree and heap of presents
Quelle: http://xkcd.com/835/
Informatik 2
Effiziente Sortieralgorithmen
Heaps
Heapsort
Ein Array lässt sich mit einem Max-Heap auch sortieren
I Stelle Heapbedingungen her. Z.B. mache eine Bubble-up
für jedes Element. Aufwand maximal O(n log n),
tatsächlich sogar O(n)
I Das oberste ist beim Max-Heap das Größte Element. Statt
es mit dem letzten Element zu überschreiben, vertausche
es mit diesem und verringere die Heapgröße um 1 ohne
den Speicher freizugeben. Damit ist das größte Element
gerade außerhalb des Heapbereichs.
I Mache dies solange bis der Heap nur noch ein Element
hat. Aufwand dafür liegt in O(n log n)
I Damit lässt sich ein Array in jedem Fall in O(n log n)
sortieren ohne zusätzlichen Speicher zu benötigen !
I In der Praxis so aber langsamer als Quicksort.
Informatik 2
Strategien
Backtracking
Backtracking
Backtracking ist einfach:
I
Basiert auf dem Trial and Error Prinzip
I
Es wird versucht eine Teillösung schrittweise zu einer
Gesamtlösung auszubauen.
I
Ist absehbar, daß eine Teillösung nicht weiter führt wird der
letzte Schritt zurückgenommen und eine alternative
Teillösung probiert.
I
So werden alle Lösungswege ausprobiert.
I
Wird am einfachsten rekursiv programmiert.
Informatik 2
Strategien
Backtracking
Pseudocode (aus Wikipedia)
Funktion FindeLoesung (Stufe, Vektor)
I wiederhole, solange es noch Teil-Lösungsschritte gibt:
I
I
wähle neuen Teil-Lösungsschritt
falls Wahl gültig
I
I
I
erweitere Vektor um Wahl
falls Vektor vollsändig : return true;
ansonsten:
falls FindeLoesung(Stufe+1, Vektor) return true
sonst mache Wahl rückgängig
Kein neuer Teil-Lösungsschritt mehr: return false
Informatik 2
Strategien
Backtracking
Beispiel 1: Labyrinth
I
Es soll der Weg zum Ausgang gefunden werden (ohne
Karte natürlich).
I
Gibt es mehr als einen möglichen Weg wähle einen aus
und gehe diesen weiter.
I
Bei einer Sackgasse gehe zurück zur letzten Kreuzung
und nimm dort einen noch nicht gegangenen Weg.
I
Wurden an dieser Kreuzung schon alle Wege gegangen,
gehe zu der Kreuzung davor zurück und wiederhole das
Spiel
I
Irgendwann wird man dann am Ausgang ankommen.
I
Mit etwas Pech hat man davor jeden einzelnen Meter des
Labyrinths kennengelernt.
Informatik 2
Strategien
Backtracking
Beispiel 2: Damenproblem
I
I
Setze n Damen auf ein n × n Brett, so daß keine
geschlagen werden kann.
2
Es gibt insgesamt nn Stellungen von denen die meisten
keinen Sinn machen.
I
Setze eine Dame (wenn nicht schon alle gesetzt sind) an
eine Stelle an der sie nicht geschlagen werden kann.
I
Gibt es keine solche Stelle, setze die zuvor gesetzte Dame
an eine andere Stelle (wenn das nicht geht die vorvorletzte
usw.)
I
für n = 8 gibt es 92, für n = 12 gibt es 14200 Lösungen.
Informatik 2
Strategien
Backtracking
Beispiel 3: Rucksackproblem
I
Eines der klassischen NP-vollständigen Probleme
I
Gegeben ist ein Rucksack mit Tragfähigkeit B
I
Außerdem N Gegenstände mit Werten und Gewichten
I
Gegenstände sollen so ausgewählt werden, daß der
Gesamtwert maximal wird, aber das Gesamtgewicht die
Tragfähigkeit des Rucksacks nicht überschreitet.
I
"Kreuzung": Gegenstand aufnehmen oder nicht, dann auf
zum nächsten.
I
"Sackgasse": Rucksack ist voll bzw. alle Gegenstände
drin. Nicht vergessen den Wert mit dem aktuellen
Optimum zu vergleichen.
Informatik 2
Strategien
Backtracking
Aufwand
I
Sehr häufig wird der gesamte Lösungsraum
durchschritten.
I
Das bedeutet dann oft eine exponentielle Laufzeit (das
Rucksackproblem z.B. ist in O(2n ), da für jeden
Gegenstand ausprobiert werden muss, ob er drin ist oder
nicht)
I
Ab und zu kann man Lösungen von vornherein
ausschließen (siehe z.B. Damenproblem).
Informatik 2
Strategien
Dynamische Programmierung
Dynamische Programmierung
I
I
I
I
Optimierungsproblem: Bestimme zu einer gegebenen
Zielfunktion, den “Wert” zu dem diese Funktion maximal
(oder minimal) wird.
Kann für Optimierungsprobleme eingesetzt werden, das
sich aus vielen gleichartigen Teilproblemen besteht und
eine optimale Lösung sich aus optimalen Lösungen der
Teilprobleme zusammensetzt.
Also: Bestimme die optimalen Lösungen der kleinsten
Teilprobleme direkt und setze diese zur Lösung eines
nächstgrößeren Teilproblems zusammen (usw.).
Einmal berechnete Teilergebnisse werden in einer Tabelle
gespeichert, damit sie nicht nochmal berechnet werden
müssen, falls sie benötigt werden. (Vermeidet unnötige
Rekursionen)
Informatik 2
Strategien
Dynamische Programmierung
Beispiel: Noch mal Rucksackproblem
I
Sei B die Gewichtsschranke, wi der Wert von Gegenstand i, und
gi das Gewicht, n Anzahl.
I
R(i, j): maximaler Nutzwert bei maximalem Gesamtgewicht j
unter Nutzung der Gegenstände i, . . . n, gesucht ist also R(1, B)
I
R(i, j) lässt sich wie folgt berechnen: Ist gi ≤ j
I
I
dann ist R(i, j) = max(wi + R(i + 1, j − gi ), R(i + 1, j))
sonst ist R(i, j) = R(i + 1, j)
I
Mit R(n + 1, 0 . . . B) = 0 und R(1 . . . n, 0) = 0 lässt sich der
Algorithmus vereinfachen.
I
Es müssen alle n · B Werte berechnet werden: O(n · B)
Informatik 2
Strategien
Greedy Algorithmen
Greedy Algorithmen
I
Baue schrittweise eine Lösung auf, indem in jedem Schritt
der momentan "profit"reichste Weg gegangen wird (womit
das Gesamtproblem verkleinert wird).
I
Dabei wird gehofft, daß die Wahl lokaler Optima auch zu
einem globalen Optimum führt.
I
Es wird das Betrachten aller Möglichkeiten vermieden,
dafür ist die Lösung aber häufig nicht optimal, gibt aber oft
eine gute Näherung der optimalen Lösung
I
Wenn die optimale Lösung für ein Problem so berechnet
werden kann, ist es üblicherweise die schnellste Methode.
Informatik 2
Strategien
Greedy Algorithmen
Münzproblem
I
I
Ermittele die kleinste Anzahl an Cent-Münzen für einen
bestimmten Betrag
Mit 50, 20, 10, 5, 2, 1 Cent Münzen funktioniert die gierige
Methode:
I
I
I
I
I
Nimm die größtmögliche Münze, die kleiner als der Betrag
ist.
Wiederhole das Spiel mit dem Restbetrag so lange bis der
Zielbetrag erreicht ist.
Mit 1, 7, 10 Cent Münzen würde er scheitern (bei 14 würde
er 10+1+1+1+1, statt 7+7 liefern)
(Es wird z.B. kritisch, wenn 2*v[i] > v[i+1] und 2*v[i] !=
v[i+1] + v[j])
Es muss für jede Münze M gelten: Ein Betrag B > C kann
ohne Nutzung von C nicht optimal zusammengesetzt
werden.
Informatik 2
Strategien
Greedy Algorithmen
Beispiel: Huffman Encoding
I
Ist ein Verfahren für verlustfreie Kompression
I
Dabei wird jedes Zeichen einzeln so kodiert, daß ein häufig
vorkommendes Zeichen möglichst wenige Bits benötigt.
I
Natürlich soll auch eine eindeutige Dekompression möglich sein.
I
Gegeben sei ein Alphabet A = {a1 , a2 , . . . , an } und die
dazugehörigen Gewichte w1 , . . . , wn (geben die Häufigkeiten der
Buchstaben an, entweder relativ oder
Pn absolut). Die einzelnen
Codes seien c1 , . . . , cn . Ziel ist es i=1 wi · Len(ci ) zu
minimieren. Dabei ist Len(ci ) die Anzahl Bits für ci .
I
Der Algorithmus baut einen Binärbaum auf.
Informatik 2
Strategien
Greedy Algorithmen
I
I
Generiere für jedes ai ein Blatt mit ihrem jeweiligen
Gewicht
Solange es mehr als einen Baum gibt
I
I
I
Nimm die beiden Knoten mit den geringsten Gewichten
Generiere einen Elternknoten, der die beiden verbindet und
als Gewicht die Summe der beiden Kinderknoten hat
Der entstandene Elternknoten kommt dann natürlich wieder
ins Spiel
I
der Weg zum entsprechenden ai Blatt im entstandenen
Baum ergibt dann den Code des jeweiligen Zeichens (links
runter entspricht 0, rechts runter 1)
I
Beim Dekodieren muss man einfach den
“Richtungsanweisungen” des Codes im Baum folgen.
Informatik 2
Graphen
Kurze Einführung
Definition
Ein Graph ist ein Tupel (V,E) mit
I
V ist die Menge von Knoten (Vertices)
I
E ist die Menge der Kanten (Edges), welche Knoten
miteinander verbindet und somit eine Teilmenge aus
V × V.
Ein Graph kann
I
ungerichtet sein: es zählt nur, ob eine Kante zwischen 2
Knoten existiert oder nicht.
I
gerichtet sein: es kommt auf Start- und Zielknoten an. So
kann es eine Kante von A nach B und von B nach A
geben, die unterschiedlich sind.
I
gewichtet sein: jeder Kante wird ein Wert zugeordnet.
Ein Baum ist also ein spezieller Graph.
Informatik 2
Graphen
Kurze Einführung
Begriffe
I
Teilgraph eines Graphen G: enthält nur Knoten und Kanten
die auch in G vorkommen (Teilmenge)
I
Weg / Knotenfolge: Eine Folge von Knoten v1 , . . . , vn bei
der zwei aufeinanderfolgende Knoten durch eine Kante
verbunden sind.
I
Pfad: Ein Weg, in der alle Knoten unterschiedlich sind
I
Zyklus: Ein Weg, bei dem v1 = vn ist
I
zyklischer / azyklischer Graph: ein gerichteter Graph, der
mind. einen / keinen Zyklus enthält
I
zusammenhängender Graph: zwischen zwei beliebigen
Knoten existiert ein Weg.
Informatik 2
Graphen
Kurze Einführung
Ein Beispiel eines gerichteten Graphen
V = {A, B, C, D, E, F }
E = {(A, B), (B, C), (C, E), (D, B), (E, D), (E, F ), }
Informatik 2
Graphen
Kurze Einführung
Ein praktisches Beispiel
V = {Frankfurt, Stuttgart, Mannheim, . . . }
E = {(Stuttgart, Nuernberg), (Karlsruhe, Mannheim), . . . }
g((Frankfurt, Mannheim)) = 85, . . .
Informatik 2
Graphen
Repräsentation von Graphen
Inzidenzmatrix
I
Eine Matrix (2-dimensionales Array) der Größe n × m bei n
Knoten und m Kanten.
I
Ist der Knoten i an der Kante j beteiligt, so gibt es in der
j.-ten Zeile und i.-ten Spalte einen Eintrag ungleich 0
I
Bei einem ungerichteten Graphen ist der Wert 1
I
Bei gerichteten Graphen, kommt es darauf an, ob der
Knoten der Start oder das Ziel ist. Im ersten Fall ist der
Wert 1, beim zweiten Fall -1.
I
Jede Spalte hat also maximal 2 Werte ungleich 0 (genau
2, wenn der Knoten angebunden ist)
I
Lohnen sich, wenn es wenige Kanten gibt.
Informatik 2
Graphen
Repräsentation von Graphen
Adjazenzmatrix
I
Eine Matrix der Göße n × n bei einem Graph mit n Knoten
und m Kanten.
I
Für jede Kante von Knoten i zu Knoten j, wird ein Wert in
der i.-ten Zeile und j.-ten Spalte eingetragen.
I
Bei Graphen, die nicht gewichtet sind, ist der Wert 1, wenn
es eine Kante zwischen beiden gibt und 0, wenn es keine
gibt.
I
Dieser Wert kann bei einem kantengewichteten Graphen
das Gewicht sein, wenn es eine Kante zwischen beiden
gibt. Gibt es keine Kante muß es ein Wert sein, der nicht
als Gewicht vorkommt (häufig 0).
I
Bei ungerichteten Graphen ist die Adjazenzmatrix
symmetrisch
Informatik 2
Graphen
Repräsentation von Graphen
Adjazenzliste
I
I
I
1
2
3
4
5
6
7
8
9
10
Eine Liste aller Knoten (bei bekannter Anzahl an Knoten
als Array implementiert),
Jeder Knoten besitzt eine Liste von Nachfolgern bzw. von
Kanten
Bei gewichteten Graphen kommt noch für jeden
Nachfolger ein Gewicht dazu
struct Node {
Edge* edges;
//Node* nextNode;
};
struct Edge {
int targetNode;
double weight;
Edge* nextEdge;
};
Node adjList[numberOfNodes];
Informatik 2
Graphen
Algorithmen
Suche des kürzesten Wegs
I
Problem: Wir haben einen gewichteten Graphen mit
positiven Kantengewichten und es ist der kürzeste Weg
von einem Startknoten zu einem Endknoten (oder allen
anderen) gesucht.
I
Die Lösung die vorgestellt wird, wurde von Edsger Dijkstra
1959 veröffentlicht, und wird daher auch gerne als Dijkstra
Algorithmus bezeichnet.
I
Gehört zu den Greedy Algorithmen, die auch wirklich
immer die optimale Lösung finden.
Informatik 2
Graphen
Algorithmen
Idee
I
Speichere für jeden Knoten den Abstand vom Startknoten
und den Vorgänger, sowie ob er schon besucht wurde oder
nicht.
I
Am Anfang ist der Abstand “unendlich” (außer beim
Startknoten, da ist er 0) und es gibt keinen (gültigen)
Vorgänger.
Solange es noch nicht besuchte Knoten gibt, wähle den
mit dem geringsten Abstand aus
I
I
I
Berechne für alle noch nicht besuchten Nachfolger dieses
Knoten die Summe des Kantengewichts und des
Abstandswertes
Ist dieser Wert für einen Nachfolger kleiner als der
bisherige Abstand, so aktualisiere den Abstandswert und
den Vorgänger
Informatik 2
Graphen
Algorithmen
Umsetzung 1
I
I
Es wird also schrittweise ein Baum aufgebaut, und ist ein
Knoten mit seinem Vorgänger mal in diesem Baum, bleibt
das auch so.
Die Knoten lassen sich in drei Kategorien einteilen:
I
I
I
Schon eingefügte Knoten (Closed List)
Knoten die sich über schon eingefügte Knoten erreichen
lassen (Randknoten)
Rest (noch nicht “entdeckte” Knoten)
I
Explizit verwaltet werden üblicherweise nur die
Randknoten.
I
Die benötigten Operationen für die Menge der Randknoten
ist: Hinzufügen (insert), Minimum löschen (delete-min),
Knotenschlüssel verringern (decrease-key)
Informatik 2
Graphen
Algorithmen
Umsetzung 2
function D IJKSTRA(graph, start)
Initialisiere Abstand und Vorgaenger
Abstand[start] = 0;
Initialisiere Randknoten
insert (Randknoten, start, 0)
while IstNichtLeer (Randknoten) do
minKnoten ← delete − min(Randknoten)
for all nachfolger von minKnoten do
alternativ ← Abstand[minKnoten] + Distanz(minKnoten, nachfolger )
if alternativ < Abstand[nachfolger ] then
Abstand[nachfolger ] ← alternativ
if IstEnthalten (Randknoten, nachfolger) then
decrease-key (Randknoten, nachfolger, alternativ)
else
insert (Randknoten, nachfolger, alternativ)
end if
end if
end for
end while
end function
Informatik 2
Graphen
Algorithmen
Aufwandsabschätzung
Anzahl der Operationen für einen zusammenhängenden Graphen mit n
Knoten und m Kanten im worst case:
I insert: n mal (jeder Knoten kommt einmal in die Randmenge)
I delete-min: n mal (jeder Knoten wird einmal rausgeholt)
I decrease-key: m mal (für jede Kante)
Bei Implementation der Randmenge als Liste mit n Elementen:
I insert: O(1) (an den Anfang einfügen)
I delete-min: O(n) (suche Minimum aus allen Elementen)
I decrease-key: O(1) bei Direktzugriff auf Element
I Insgesamt: O(n · n + n · 1 + m · 1) = O(n2 + n + m)
Bei Implementation der Randmenge als MinHeap:
I insert: O(log n)
I delete-min: O(log n)
I decrease-key: O(log n)
I Insgesamt: O(n · log n + n · log n + m log m) = O((2n log n + m log m)
Informatik 2
Graphen
Algorithmen
A* Algorithmus
I
Soll den schnellsten Weg zwischen zwei Knoten auch
möglichst schnell finden.
I
Der Dijkstra Algorithmus weiß außer dem Namen nichts
vom Ziel
I
Dementsprechend kann es passieren, daß er vom
Startknoten die kürzesten Strecken zu allen anderen
Knoten berechnet, bevor der Zielknoten dran ist.
I
Um dies einzuschränken soll die verbleibende Distanz zum
Zielknoten mit berücksichtigt werden.
I
Ansonsten fast wie der Dijkstra Algorithmus
Informatik 2
Graphen
Algorithmen
Heuristik
I
Die Kosten eines Knotens sind:
f (x) = g(x) + h(x)
I
g(x): Bisheriger Abstand des Knotens x vom Startknoten
I
h(x): Heuristik zur Abschätzung der Distanz vom Knoten x
zum Zielknoten
I
Dijkstra: h(x) = 0
I
Die Heuristik h(x) muß zulässig (admissible) sein, d.h. sie
überschätzt nie die Distanz zum Zielknoten.
I
Der Abstand über die "Luftlinie" ist so eine Heuristik.
Informatik 2
Graphen
Algorithmen
Monotonie
I
Eine weitere erstrebenswerte Eigenschaft einer Heuristik
ist die Monotonie
I
∀(x, y ) ∈ E : h(x) ≤ d(x, y ) + h(y )
d(x, y ) sei der wirkliche Abstand von x nach y .
I
Jede monotone Heuristik ist auch zulässig.
I
Luftlinienabstand ist monoton.
I
Ist die gewählte Heuristik nicht monoton, so müssen
Knoten aus der Closed List evtl. noch einmal besucht
werden.
I
Bei einer monotonen Heuristik kann einfach der Dijkstra
mit der Kostenfunktion f (x) durchgezogen werden.
Herunterladen