Algorithmen & Datenstrukturen
Wintersemester 2002/2003

Martin Steffen

1  Einleitung

Literatur: Baut auf Kapitel 1 von [CLR90] auf.
Inhalt Algorithmus ·Analyse von Algorithmen ·Zeitkomplexität und Speicherkomplexität ·Algorithmenentwurf
Aufbau
Algorithmus
Berechnungsproblem: Sortieren
Beispiel: Insertion-Sort


Insertion_sort(A)

  for j = 2 to length[A]
    do key := A[j];
         i :=  j-1;
       while   i > 0 and A[i] > key
          do
               A[i+1] := A[i]
                    i := i-1;

       A[i+1] := key;

  
   





Analyse von Algorithmen
Analyse des Sortierens durch Einfügen




Insertion_sort(A)

 for j = 2 to length[A]            // c1    n
   do key := A[j];                 // c2    n-1
        i :=  j-1;                 // c4    n-1    
      while  i > 0 and A[i] > key  // c5    sum(j=2 to n) t_j
         do
              A[i+1] := A[i]       // c6    sum(j=2 to n) (t_j-1)
                   i := i-1;       // c7          -"-
         od
       A[i+1] := key;              // c8    n-1

Analyse (2)




Insertion_sort(A)

 for j = 2 to length[A]            // c1    n
   do key := A[j];                 // c2    n-1
        i :=  j-1;                 // c4    n-1    
      while  i > 0 and A[i] > key  // c5    sum(j=2 to n) t_j
         do
              A[i+1] := A[i]       // c6    sum(j=2 to n) (t_j-1)
                   i := i-1;       // c7          -"-
         od
       A[i+1] := key;              // c8    n-1


T(n) = c1 n + (c2+c4+c8)(n-1) +
   
c5
n
å
j=2
tj + (c6+c7)
n
å
j=2
(tj-1)

Analyse (3)
    tj T(n)
bester Fall sortiert 1 a n + b
schlechtester Fall rückwärts sortiert j a n2 + b n + c
Algorithmenentwurf


D&C: Mergesort
Mergesort (code)




Merge_Sort(A,p,r)
  if p < r
  then   q := floor(p+r/2);   // the ``middle''
         Merge_Sort(A,p,q);
         Merge_Sort(A,q+1,r);
         Merge(A,p,q,r)



Analyse von Merge-Sort

2  Sortieren

Literatur: Kapitel 7 und 8 aus [CLR90]. Knuth [Knu73b] enthält mehr über Sortieren als man wissen will. Heapsort stammt von Williams [Wil64], Quicksort von Tony Hoare [Hoa62]
Inhalt Heaps ·Heapsort ·Quicksort
Heap-Datenstruktur


Implementierung: Heaps als Array
Heapsort: Aufbau
Einfügen in den Heap


Heapify (A, i)                  
  l := Left(i); r := Right(i);  // Left(i), Right(i) are heaps   

  if l <= heapsize(A) and A[l] > A[i]   // finde das maximum
     then largest := l  else largest := i;
  if r <= heapsize(A) and  A[r] > A[largest]
     then largest := r;

  if largest != i                 // Verletzung der Heapbedingung?
     then exchange (A[i] , A[largest]);
          Heapify(A, largest);





Aufbau des Heaps


Build-Heap(A)  
  heapsize[A]  := length[A];
  for i= floor(length(A)/2)) downto 1  // bottom-up
  do  heapify(A,i);





Heapsort


Heapsort(A)

   build_heap(A);                       // build initial heap
   for i = length(A) downto 2           
   do  exchange A[1] A[i];              // extract maximum
       heapsize[A] := heapsize[A] - 1;  // decrease size of heap
       Heapify(A,1);                    // repair heap condition



Quicksort


Quicksort (2)


language=Ocaml,escapeinside=(**)

(* $Id: quicksort.ml,v 1.8 2001/11/11 12:40:19 softtech Exp $ *)

(* A functional implementation of quicksort. The divide-and-conquer
   approach is visible in the recursive inner $\ocwlowerid{sort}$-function:
   It chooses as pivot the first element. Using the auxiliary function
   $\ocwlowerid{filter}$,\footnote{The function is directly available from
   the standard-library. It's included here for sake of reference.} it
   splits the remainder of the list into two halves, the elements less or
   equal the pivot and those larger than the pivot.  The sublists are
   sorted and, together with the pivot in the middle, put together to the
   sorted list.

*)

(*i*)
open Basic;;
open Sort;;
(*i*)



module QuicksortFun (E: ORDERED) : (SORT with type elem = E.t) =
    struct
      type elem = E.t 
     
      let rec filter p l = match l with  (* auxiliary function *)
     []     -> []
      | x :: tl ->  
   if   (p x) 
   then  x::(filter p tl)
   else  filter p tl
     
      let rec sort (l : elem list) =   
 match l with  
          []  -> []
 | x :: tl  ->                                   (* x = pivot *)
     sort (filter (function y -> E.lt(y,x)) tl)
     @ [x] @                                     
     sort (filter (function y -> E.geq(y,x)) tl)
    end







Quicksort mit arrays (1)




Quicksort(A,p,r)
if  p < r 
    then   q := Partition(A,p,r)   
           Quicksort(A,p,q);
           Quicksort(A,q+1,r);





Quicksort mit arrays: Partitionieren

I1 A[i'] £ x £ A[j'] für alle i'< i, j' > j.
I2 A[i'] £ x £ A[j'] für alle i'£ i, j' ³ j.


Partition(A,p,r)                      /* p <= r         */
  x := A[p];
  i := p-1;
  j := r+1;
 
  while true                          /* invariant I_2    */
  do     repeat j := j-1
           until A[j] <= x;           /* non-strict comp. */
         repeat i := i+1
           until A[i] >= x;
         if   i < j                   /* invariant I_1    */
         then exchange A[i] <-> A[j]  /* invariant I_2    */
         else return   j;             /* j <= i           */
  od;
  
         



Analyse von Quicksort: worst case
Analyse von Quicksort: best case


3  Lineares Sortieren

Literatur: Kapitel 9 [CLR90].
Inhalt O-Notation ·Einführung ·Vergleichssortieren ·Counting sort ·Bucket sort
Einführung
Sortieren: Überblick

Verfahren Tmin Tmax Tav
Auswahl O(n2) O(n2) O(n2)
Einfügen O(n) O(n2) O(n2)
Bubblesort O(n) O(n2) O(n2)
Mischen O(n logn) O(n logn) O(nlogn)
direktes Mischen O(n logn) O(n logn) O(nlogn)
natürliches Mischen O(n) O(n logn) O(nlogn)
Quicksort O(nlogn) O(n2) O(nlogn)
Heapsort O(n logn) O(n logn) O(n logn)

Table 1: Vergleich


Abschätzung für Vergleichssortieren
Vergleichssortieren: worst-case
Theorem 1  [Untere Schranke für worst-case]   Die Höhe ein Entscheidungsbaums der n Elemente sortiert besitzt eine asymptotische untere Schranke der Größe O(n lgn).

Abschätzung der Laufzeit(2)
Beweis:[of Theorem 1]



Lineares Sortieren: Counting Sort
Counting Sort




Counting-Sort(A,B,k)
  for i := 1 to k do C[i] := 0; /* initialize */

  for j := 1 to length[A]
  do  C[A[j]] := C[A[j]] + 1  /* C[i]: Anzahl der Elemente = i */

  for i := 2 to k             /* Erlaubter Input von 1...k     */
  do  C[i] := C[i] + C[i-1];  /* C[i]: Anzahl Elemente <= i    */

  for j := length[A]  downto  1
  do
       B[C[A[j]]] :=   A[j];  /* C[A[j]] gibt Pos. in B        */
         C[A[j]]  := C[A[j]] - 1;   
  od;



Analyse von Counting Sort
  1. erste Schleife: O(k)
  2. zweite Schleife: O(n)
  3. dritte Schleife: O(n)
Bucketsort
Bucketsort (2)




BucketSort(A) 
  n := length(A);

  for i=1 to n
    do insert A[i] into list B[floor (n * A[i])];

  for i=0 to n-1
    do sort list B[i] with insertion sort;

  concatenate lists B[0], B[1], B[n-1] together;



Bucketsort: Analyse

4  Analyse

Literatur: Verschiedene Kapitel aus [CLR90]. Daneben Abschnitt 2.5 aus [Heu97].
Inhalt Analyse von Divide and Conquer ·lineare, O(nlogn), polynomielle Komplexität
Laufzeitanalyse für Divide & Conquer
Lösen der Rekurrenzgleichung


Sei n = bk. Auflösen durch Iteration.
T(n) =
a T(
n
b
) + f(n)
  =
a2 T(
n
b2
) + a f(
n
b
) + f(n)
...
  =
ak T(
n
bk
) +
k-1
å
i=0
ai f(
n
bi
)
  =
alogb n T ( 1) ) +
(logb n)-1
å
i=0
ai f(
n
bi
)
  =
alogb n T(1) +
(logb n)-1
å
i=0
ai f(
n
bi
)
  =
d nlogb a +
(logb n)-1
å
i=0
ai f(
n
bi
)
Spezialfall: lineare Zeitkomplexität


T(n) =
d nlogb a +
(logb n)-1
å
i=0
ai f(
n
bi
)
  =
d nlogb a +
(logb n)-1
å
i=0
c ai
n
bi
  =
d nlogb a + c n
(logb n)-1
å
i=0
æ
ç
ç
è
a
b
ö
÷
÷
ø
i



 
  £ O(n)

Spezialfall: O(nlogn)

T(n) =
d nlogb a +
(logb n)-1
å
i=0
c ai
n
bi
  =
d nlogb a + c n
(logb n)-1
å
i=0
æ
ç
ç
è
a
b
ö
÷
÷
ø
i



 
  =
d n + c n
(logb n)-1
å
i=0
1
  £ O(nlogn)
Spezialfall: polynomiale Laufzeit

T(n) =
d nlogb a +
(logb n)-1
å
i=0
c æ
ç
ç
è
a
b
ö
÷
÷
ø
i



 
  £
d nlogb a + c n
æ
ç
ç
è
a
b
ö
÷
÷
ø
logb n



 
-1
a
b
-1
  £
O æ
ç
ç
ç
ç
ç
è
nlogb a + n æ
ç
ç
è
a
b
ö
÷
÷
ø
logb n



 
ö
÷
÷
÷
÷
÷
ø
  £
O æ
ç
ç
è
nlogb a + n
alogb n
blogb n
ö
÷
÷
ø
  £
O ( nlogb a )


5  Elementare Datenstrukturen

Literatur: Kapitel 11 [CLR90].
Inhalt Stacks und Queues ·verzeigerte Listen ·Bäume ·Implementierungsmöglichkeiten
Einleitung
Stacks und Queues
Stack als Array


Stack-empty(S)
  if top == 0 then return true else false;

Push(S,x)
  top    := top + 1;
  S[top] := x;

Pop(S)
  if Stack-empty(S)
  then error "underflow"
  else top  := top  -1;
       return S[top+1];




Queues als Array


Enqueue(Q,x) =
  Q[tail] := x;
  if tail == length(Q)
     then tail := 1       // start again with 1
     else tail := tail + 1;


Dequeue(Q) =
  x := Q[head]
  if head == length(Q)
     then head := 1        // start again with 1
     else head := head + 1
  return x;
  



Verzeigerte Listen
Verzeigerte Listen (2)

Operation Komplexität
(vorne) Einfügen O(1)
Löschen (bei geg. Element) O(1)
Suchen O(n)

dopelt verzeigerte Listen (3)




List-search(L,k)     // k: key  
  x  := head;
  while  x != nil and x.key != k
     do  x := next(x);

  return x;





List-insert(L,x)            // vorne Einfuegen
  next(x) := head;
  if head != nil then prev(head) := x
     head := x;
  prev(x) := nil;

List-delete(L,x)
  if prev(x) != nil
     then next(prev(x)) := next(x)
     else          head := next(x)
  if next(x) != nil
     then prev(next(x)) := prev(x); 





6  Hashstrukturen

Literatur: Kapitel 12 aus [CLR90]. Hashfunktionen werden auch in [Knu73b] diskutiert.
Inhalt Hashstrukturen ·Hashing mit externer Verkettung ·Hashing mit offener Adressierung ·Hashfunktionen
Einleitung
Adreßtabellen mit direktem Zugriff


Search(T,k)   = return T[k];

Insert(T,x)   = T[key(x)] := x;

Delete(T,x)   = T[key(x)] := nil;

Hashtabelle
Kollisionsauflösung: Verkettung


Insert(T,x) = T[h(key(x))] := insert x at the head of T[h(key)];

Search(T,k) = Search for an element with key k in T[h(k)];

Delete(T,x) = Delete x  from list  T[h(k)];




Analyse

Definition 1  [Belegungsfaktor]   Der Belegungsfaktor a (load factor) einer Hashtabelle T[0,..., m-1] ist definiert als
a =
n
m
,
wobei n gleich die Anzahl der gespeicherten Elemente ist.
Analyse (2)
Satz 1   Das erfolgreiche sowie erfolglose Suchen in einer Hashtabelle mit Verkettung und unter der Annahme einfachen, uniformen Hashings benötigt im Mittel O(1+a) Zeitaufwand.

Beweis: Unterscheidung in erfolglose und erfolgreiche Suche. erfolglos erfolgreich Annahmen: Ansatz:
|Zugriffe beim Finden von e| = |Zugriffe beim Einfügen von e| +1
Durschnittliche Länge beim (=vor dem) Einfügen des i-ten Elements: i-1/m im Durchschnitt: 1/n åi=1n
æ
ç
ç
è
1+
i-1
m
ö
÷
÷
ø
= ... 1 + a/2 - 1/2m Þ O(1+a)


Hashfunktionen


Offene Adressierung
Offene Adressierung: Hashfunktionen

7  Binäre Bäume

Literatur: Kapitel 13 aus [CLR90]. Balancierte Bäume stammen aus [AVL62].
Inhalt Binäre Bäume ·Baumoperationen
Einleitung
Binäre Suchbäume

Definition 2  [Binärer Suchbaum]   Ein binärer Suchbaum ist ein binärer Baum (left, right, (evtl. parent)) bei dem für alle Knoten n gilt:
key(l) £ key(n) £ key(r).
wobei l und r beliebige Knoten aus dem rechten bzw. linken Unterbaum von n sind
Durchlaufen


InOrder-Tree-Walk(t) =
  if   t != nil
  then InOrder-Tree-Walk(left(t));
       print (key(t));
       InOrder-Tree-Walk(right(t));



Baumoperationen: Suchen


Search(t,k) =                // t : tree, k : key
  if         t == nil    then raise error("Not found")
  else if    k == key(t) then return t
  else if    k <  key(t) 
       then Search( left(t),k)
       else Search(right(t),k)



Baumoperationen: Suchen (2)
Hier das Ganze nochmals iterativ:6



Search(t,k) =                // t : tree, k : key
  while (t != nil) and (k != key(t))
  do
    if k < key(t)
       then t :=  left(t)
       else t := right(t)
  od;
  if   (t == nil) 
  then raise error("Not Found")
  else return t



Baumoperationen: Einfügen
Einfügen: code


Insert(T,z) =              // T : tree, z : node to insert
  y := nil; x := root(T);  // two aux. variables
  while  x != nil          // find the right leaf
  do
    y := x;                // y to remember the father
    if   key(z) < key(x)
    then x := left(x) 
    else x := right(x)
  od;                     
  p(z) := y;               // => x is nil, y its predecessor
  if   y = nil             // special case: y = root
  then root(T) := z
  else if   key(z) < key(y)// insert z at the appropriate
       then  left(y) := z  // place as new leaf
       else right(y) := z 



Baumoperationen: Löschen
Baumnachfolger & Minimum




Minimum(z) = // input: binary search tree, output: node with min. key
  while left(z) $\not=$ nil 
  do  z := left(z)
  od
  return z;



Sucessor(z) 
  if   right(z) $\not=$ nil
  then return Minimum(right(z))
  y:=parent(z);
  while y $\not=$ nil and z=right(y)
  do
 z := y;
 y := parent(y);
  od
  return y;





Delete(T,z)           // T: tree, z: node to delete
  if   left(z) = nil or right(z) = nil
    then y := z              // y replaces  x
    else y := Successor(z);  // where: y has <= 1 child!

  if left(y) != nil          // determine the child x
     then x :=  left(y)      // of  y (might be empty)
     else x := right(y);
                                 // change the pointers
  if x != nil then p(x) = p(y);  // remove y  => x has
                                 // new parent
  if p(y) = nil                  // special case: root
    then root(t) = x
    else if  y = left(p(y))
           then  left(p(y)) := x
           else right(p(y)) := x  // y ist now removed from
                                  // its orig. place

  if y != z then key(z) := key(y);// replace z by y

  return y;                      // and that's it



8  Rot-schwarz-Bäume

Literatur: Kapitel 13 aus [CLR90]. Die Rot/Schwarz-Bäume wurden von [Bay72] eingeführt.
Inhalt Rot/Schwarz-Bäume ·Rotation ·Erhalt der Rot-Schwarzeigenschaft bei Einfügen und Löschen
Einführung
Definition


Definition 3   Rot/schwarz-Bäume sind binäre Suchbäume mit einem Bit Zusatzinformation: der Farbe und folgenden Bedingungen:
  1. jeder Knoten ist entweder rot oder schwarz
  2. Blätter (nil) sind schwarz
  3. die Kinder eines roten Knotens sind schwarz
  4. jeder einfache Pfad von einem gegebenen Knoten zu einem Blatt enthält die selbe Anzahl an schwarzen Knoten8

Lemma 2   Ein rot-schwarz Baum mit n internen Knoten hat eine Höhe von höchstens 2lg(n+1).
Rotation


Rotate-l(t,x) =                   // t = Baum, x = Knoten
  if    (x = nil) or (right(x) = nil)
  then  raise error "illegal rotation"
  else  y := right(x);          // rette y
        p(y) := p(x);              // ver"andere y's Verwandschaft
        if   y != nil              
        then right(x) := left(y);  // beta
        if   (left(y)) != nil
        then p(left(y)) := x;
 
        if   root(t) = x           // Spezialfall
        then root(t) := y
        else if   x = left(p(x))  // p(x) definiert
             then  left(p(x)) := y
             else right(p(x)) := y

        left(y) := x;             // zum Schluss: x als Kind von y
        p(x)    := y;               



Einfügen
Einfügen: drei Fälle (+ 3 symmetrische)
Die Graphik sind leider noch nicht nach HTML übersetzbar



insert(T,x) 
  tree_insert(T,x);                          // normal einfuegen 
  color(x)  := red                           // am Anfang rot
 
  while x != root(t) and color(p(x)) = red   // Rot-verletzung
  do
    if  p(x) = left(p(p(x));                 // Vater ist linker Sohn
       then  y := right(p(p(x))              // y = Onkel, d.h.
                                             // rechter Sohn des Opas
             if  color(y) = red              // Onkel rot
                then  color(p(x))  := black; // => umfaerben
                      color(y)     := black;
                     color(p(p(x)) := red;
                                 x := p(p(x))
                else if x = right(p(x))      // Onkel schwarz
                        then x := p(x);      // x ist rechtes Kind
                             left_rotate(T,x) 

                     color(p(x)) := black;   // Vater <- schwarz
                     color(p(p(x)) := red;   // Opa   <- rot
                    right_rotate(T,p(p(x))); // Ausgleich
        else ....... // analog
  done;
  color(root(T)) := black;



Löschen

Siehe Übung


9  Graphen

Literatur: Kapitel 23 aus [CLR90]. Breitensuche stammt von Moore [Moo59]. Der Erfinder der Tiefensuche scheint unbekannt. Eine Algorithmus der ähnlich wie Tiefensuche in Labyrinthen arbeitet, ist der ``Algorithmus von Trémaux''. Der Oré-Algorithmus ist eine Variante der Breitensuche für Labyrinthe. Der angegebene Algorithmus für die starken Zusammenhangskomponenten ist von Tarjan [Tar72].
Inhalt Graphen ·Repräsentierungen ·Erreichbarkeit ·Breitensuche ·Tiefensuche ·backtracking ·topologisches Sortieren ·Berechnung starker Zusammenhangskomponenten
Suchen in Graphen
Graphen und ihre Repräsentierung
Definition 4  [Graph]   Ein (gerichteter) Graph G = (V,E): V Menge von Knoten, EÍ V × V Kanten.
Adjazenzlistendarstellung
Adjazenzmatrixdarstellung
Breitensuche
Prinzip
Breitensuche (2)


bfs (G,s) =      // Graph und Quellknoten 
  for each vertex u in V[G] - {s}
  do color[u] := white
     d[u]     := $\infty$;
     p[u]     := nil
  done;
  color[s]    := gray;
  d[s]        := 0;
  p[s]        := nil;
  Q.enqueue(s);                 // beginne mit der Quelle

  while not Q.isempty()
  do
     u := Q.head();             // noch kein dequeue!
     for each v in Adj[u]       // alle Nachbarn
     do
        if   color[v] = white
        then color[v] := gray;
             d[v]     := d[u] + 1;
             p[v]     := u;
             Q.enqueue(v)      // speichere den Nachbarn
        fi;
     done;
     Q.dequeue()            // u ist abgearbeitet
     color(u) := black;     // und als fertig markiert
  done





Breitensuchalgorithmus: Analyse
Korrektheitsargument
Siehe Vorlesung

Tiefensuche
Algorithmus (1)


DFS(G)                           // Gerichteter Graph
  for all vertices $v \in V[G]$  // Initialisierung
  do 
     color[u] := white;          
      pred[u] := nil; 
  od
  time := 0;                     // Ende der Initialisierung
  for all vertices  $u \in V[G]$
  do
      if color[u] = white then DFS-Visit(u); // eigentl. Tiefensuche
  done;
 

Algorithmus (2)


DFS-visit(u)                       // Eigentliche Tiefensuche
  color[u] := gray;                // Knoten entdeckt
  time := time + 1;  d[u] := time; // merke Entdeckungszeit
  for each v in Adj[u]             // erkunde Kante (u,v)
  do  if   color[v] = white
      then pred[v] := u            // pred = Entdeckerknoten
           DFS-visit(v)            // rek. Abstieg
  done;
  color[u] := black;               // u ist fertig, merke
  time := time + 1; f[u] := time;  // die Fertigstellzeit



Tiefensuche: Analyse
Weitere Eigenschaften
[Klammerung] Gegeben zwei Knoten u, v aus G = (V, E). Es gilt genau eine der folgenden Bedingungen:
  1. [d[u],f[u]] ist echtes Teilintervall von [d[v], f[v]], u ist Nachfahre von v in Tiefensuchbaum
  2. Symmetrisch.
  3. [d[u],f[u]] und [d[v], f[v]] disjunkt
Knoten v ist echter Nachfolger von u im Tiefensuchwald gdw. d[u] < d[v] < f[v] < f[u]
Topologisches Sortieren
Zunächst ein paar Definitionen:
Definition 5   Gegeben eine binäre Relation R. Die reflexive, transitive Hülle R of R ist gegeben als die kleinste binäre Relation sodaß x R x und x R y R z impliziert x R z. Mit R+ ist die Relation R R gemeint.
Definition 6   Eine binäre Relation R Í S × S auf einer Menge S ist eine Halbordnung (Notation dann oft £ für R) wenn sie reflexiv, transitiv, und antisymmentrisch ist. Eine totale oder lineare Ordnung ist eine Halbordnung mit der zusätzlichen Eigenschaft der Vergleichbarkeit. (" x,y. x£ y oder y£ x)
Definition 7  [DAG]   Ein gerichteter, azyklischer Graph (DAG) ist ein gerichteter Graph ohne Zyklen, d.h., es für alle Knoten u, v Î V gilt: Wenn u ®+ v, dann u ¹ v.
Topologisches Sortieren (2)


Topological-Sort(G)               // G ist ein DAG

  call DFS(G) to compute  finishing-times f[v] for all v;
  as each vertex is finished, insert it onto the front of a list;
  return the linked list

Topologisches Sortieren (3)
Satz 2  [Weiße Pfade]   Gegeben Graph G. Im Tiefensuchwald für G gilt: v ist Nachfahre von u gdw. zur Zeit d[u] der Knoten v durch u auf einem weißen Pfad erreichbar ist.14

Lemma 3   Ein gerichteter Graph ist azyklisch gdw. die Tiefensuche keine Rückwärtskanten produziert.
Beweis: Sei G zyklisch Þ es gibt einen Zyklus c: w ®+ w. Sei v der erste entdeckte Knoten aus c und u sein Vorgänger im Zyklus. Zur Zeit d[v]: weißer Pfad v ®* u  Þ  u wird Nachfolger im Tiefensuchwald Þ Behauptung ((u,v) ist Rückwärtskante). Þ DFS findet Rückwärtskante (v,u), andererseits u ®* v mittels Baumkanten (weil u Vorgänger von v im Tiefensuchbaum)
Lemma 4  [Korrektheit]   Der Algorithmus angewandt auf einen DAG, liefert eine topologische Sortierung.
Beweis: Prüfen der Kompatibilität: falls u ®+ v in G, dann f[v] < f[u]. Sei (u,v) eine Kante. Wenn sie mit DFS erkundet wird, ist v nicht Grau15 Þ v ist weiß oder schwarz. v weiß v wird Nachfahre von u Þ f[v] < f[u] v schwarz Dann sofort f[v] < f[u]


Starke Zusammenhangskomponenten

Definition 8  [Starker Zusammenhang]   Gegeben gerichteter Graph G = (V, E). Eine starke Zusammenhangskomponente von G ist eine maximale Knotenmenge UÍ V mit: für alle u1, u2 Î U gilt u1®* u2 und u2®*u1.
Algorithmus




Strongly-connected-components(G)    // G: gerichteter Graph

  call DFS(G) to obtain finishing times f[u] for the vertices;

  compute $G^t$ = transpose(G)        // transponiere G

  call DFS($G^t$), but in the main loop of DFS, 
       consider the vertices in order of 
       decreasing f[u]

  output the vertices of each tree in the depth-first 
      forest from the previous step as a separated strongly 
      connected component.





Analyse
Lemma 5   Der Vorvater f(u) bzgl. einer Tiefensuche ist ein Vorfahre von u,
Für alle Knoten u gilt: u und f(u) liegen in der selben starken Zusammenhangskomponente.

Beweis: Folgt direkt aus dem Vorangegangenen und der Definition des Vorvaters.


Analyse (2)

10  Spannbäume

Literatur: Kapitel 24 aus [CLR90].
Inhalt Kantengewichte ·minimale Spannbäume·nimmersatte Strategien (``greedy'') ·Kruskals Algorithmus ·Prims Algorithmus
Minimaler Spannbaum
Spannbaumalgorithmus
Definition 9   Sei A Teilmenge eines minimalen Spannbaumes für G. Eine Kante (u,v) ist sicher für A, falls A È {(u,v)} Teilmenge eines minimalen Spannbaumes ist.



Generic-MST(G,w) =
  A := 0;
  while A does not form a spanning tree
  do 
    find an edge (u,v) that is safe wrt. A
    A := A $\cup$ {(u,v)}
  done;
  return A





Sichere Kanten
Definition 10   Ein Schnitt (S, V-S) (cut) eines ungerichteten Graphen ist eine Partition von V. Eine Kante kreuzt den Schnitt, falls ein Endpunkt in S ist und der andere in V-S. Ein Schnitt respektiert eine Menge von Kanten, wenn keine Kante den Schnitt kreuzt. Eine Kante, die einen Schnitt kreuzt, ist leicht falls ihr Gewicht von allen kreuzenden Kanten minimal ist.18
Theorem 6  

minimaler-Spannbaum-Algorithmus
Es gilt: Wenn (u,v) eine leichte Kante ist die C mit einer anderen Komponente verbindet, dann ist (u,v) sicher für A.



Kruskals Algorithmus
Kruskal (2)






mst-kruskal(G,w) 
  A := 0;                   // A: Kantenmenge
                            // Invariante: A Teil eines min. S-B
  for all $v \in  V[G]$
  do Make-Set(v) done;      // Wald aus lauter Knoten

  sort the edges of $E$ by non-decreasing weight $w$;
 
   for all edges $(u,v) \in E$ (in order)
   do
     if   Find-Set(u) $\not=$ Find-Set(v) // nicht im selben Baum?
     then 
       A := A $\cup$ {(u,v)};// fuege Kante hinzu
       Union(u,v);           // vergroebere Partition
     fi;
   done;
   return A;

Kruskal (3)
Prims Algorithmus


mst-prim(G,w,r)            // G = Graph, w = Gewichtung,  r: Wurzel
  Q := V(G);               // priority queue von Knoten
                           // Q : Knoten noch nicht im Baum

  for all $u \in  Q$ do key[u] := $\infty$ done;
  key[r] := 0;                 
  p(r)   := nil;           // r ohne parent
  while  Q $\not= \emptyset$
  do
    u := extract_min(Q);   // u dem Baum hinzugefuegt
    for all $v \in Adj(u)$ // bewerte alle Nachbarn
    do
       if   $v \in$ Q and $w(u,v) < key(v)$
       then p(v)   := u;   // parent
            key(v) := w(u,v)   
       fi
    done
  done



Prim (3)


11  Kürzeste Pfade

Literatur: Teile von Kapitel 25/26 aus [CLR90]. [Ski97]
Inhalt Einleitung ·Varianten ·Relaxation ·Dijkstras Algorithmus ·Bellman-Ford ·lineare Programmierung
Einleitung
Kürzeste Pfade: Definition

Definition 11  [Kürzester Pfad]   Gegeben G = (V, E) gerichteter Pfad, w : E ® R. Das Gewicht eines Pfades p = (v0, v1, ..., vk) ist definiert:
w(p) =
k
å
i=1
w(vi-1, vi).
Das Gewicht des kürzesten Pfades von u nach v ist das Minimum:
d(u,v) = ì
ï
í
ï
î
min{w(p) | u
p
®
 
v}
falls $ Pfad von u nach v
¥ sonst
Ein kürzester Pfad von u nach v ist ein Pfad von u nach v mit w(p) = d(u,v).

Problemvarianten
Kürzeste Pfade
Kürzeste Pfade (2)


Hauptidee: Der kürzeste Pfad zwischen zwei Knoten
enthält kürzeste Pfade als Teile
Lemma 7   geg G = (V, E), gerichtet, gewichtet mit w: E ® R. (v1,..., vn) ist ein kürzester Pfad von v1 nach vn und w = (vi,..., vj) mit 1£ i £ j £ n ein Teilpfad. Dann ist w ein kürzester Pfad von vi nach vj.
Relaxation
generische Relaxation
Dijkstra
Dijkstra (2)




Dijkstra(G,w,s)
  Intitialise(G,s);       // siehe generischer Algo.
  S := $\emptyset$;                 // Knoten mit bekannter Distanz 
  Q := V;                 // priority queue               

  while $Q \not= \emptyset$
  do
    u := extract-min(Q); 
    S := $S \cup \{u\}$;
    for all vertices $v  \in Adj(u)$
    do
        relax(u,v)
    done
  done
  



Bellmann-Ford
Bellmann-Ford (2)






Bellmann-Ford(G,w,s)              // G = (V,E), Gewichtung w

  initialize(G,s);                // s. generischer Algo.
  for i := 1 to size(V)-1
  do
    for all edges $(u,v) \in  E$
    do 
      relax(u,v) 
    done
  done;
  for all edges $(u,v) \in E$     // Test auf negative Zyklen
  do
    if d(v) > d(u) + w(u,v) 
       then return false
       else return true
  done;





12  Dynamische Programmierung

Literatur: Siehe Kapitel 43 aus [Sed90] und Kapitel 3 aus aus [Ski97]. Zu dynamischer Programmierung findet sich auch in [CLR90] etwas, vor allem in Kapitel 16, aber auch sonst "uber das Buch verteilt.
Inhalt Probleml"osung durch Zergliederung ·Optimierungsprobleme ·Ansatz der dynamischen Programmierung ·Rekursiver Probleme mit gemeinsamen Teilstrukturen ·Beispiel: lineares Partitionieren ·Beispiel: k"urzeste Pfade ·Anwendungsbereich von DB
Klassifizierung von Klassen
Dynamische Programmierung
Speicher-- vs. Laufzeit: das Karnickelproblem
Dynamische Programmierung


  1. Charakterisiere die Struktur der optimalen L"osung
  2. Definiere rekursiv die optimale L"osung
  3. Berechne den Wert der opt. L"osung bottom-up
  4. ggf: berechne/rekonstruiere die opt. L"osung selbst.


Linear Partitionieren
Linear Partitionieren (2)


Partition(S,k) =   // linear Partitionieren
  p[0] = 0;        // P = Teilsummen
  for i = 1 to n do 
    p[i] := p[i-1] + $S_i$
  done; // Initialisierung der ``Raender''
  for i = 1 to n do M[i,1] := p[i] done;
  for j = 1 to k do M[1,j] := $s_1$ done;
  for i = 2 to n 
  do
    for j = 2 to k do
      M[i,j] := $\infty$;
      for x = 1 to i-1 
      do
        s = max(M[x,j-1],p[i]-p[x]);
        if    M[i,j] > s 
        then  M[i,j] = s;    // glob. Minimum
       D[i,j] = x     // Zur Rekonstruktion 
        fi
      done
    done
  done
  





13  Kombinatorische Suche und Heuristiken

Literatur: Meist aus Kapitel 5 von [Ski97].
Inhalt Backtracking ·Einschr"ankung der Suche ·Heuristiken
Backtracking
Backtracking (2)


backtrack(A) =       // A = Vektor/Array $(a_1,\ldots ,a_n)$
  compute $S_1$;     // m"ogliche Loesungen fuer 1. Position
  k := 1;
  while k > 0 
  do
    while $S_k \not= \emptyset$
    do
      $a_k$ = naechstes Element aus $S_k$;
      $S_k := S_k\without a_k$;
      if $A = (a_1,a_2,\ldots, a_k)$ Loesung, speichere sie fi;
      k := k+1;
      berechne S_k
    done;
    k := k-1                  // ``Backtrack''
  done



Backtracking (3)


backtrack-dfs(A,k) =
  if   $A  = (a_1,a_2,\ldots, a_k)$ Loesung, 
  then speichere sie
  else k := k+1;
       compute $S_{k}$;
       while $S_{k} \not= \emptyset$
       do
         Waehle ein Element $a$ aus $S_k$.
         $a_{k} := a$;
         $S_k := S_k\without a$;
         backtrack-dfs(A, k)
       done
   fi



Beispiele f"ur kombinatorische Suche
Beschr"ankung des Suchraumes
Beispiel: Bandbreitenminimisierung
allgemeine Heuristiken

14  ``Harte'' Probleme

Literatur: Verschiedenes aus [CLR90] oder "ahnlichem. Vor allem Kapitel 36. Dazu Kapitel 11 aus [HU79]. Ein (schwieriges) Standardwerk zur Komplexit"atstheorie ist [Pap94].
Inhalt Komplexit"atsklassen P und NP ·Algorithmisch harte Probleme
Harte Probleme
Problem des Handlungsreisenden
Komplexit"atsklasse NP
Definition 12  [NP]   Die Komplexit"atsklasse NP ist die Klasse von Problemen, die nichtdeterministisch-polynomiell gel"ost werden kann.
Die Klasse NP
Reduzierbarkeit und Komplexit"atsklassen
Definition 13  [Polynomielle Reduktion]   Ein Problem L1 (Sprache) ist reduzierbar in polynomieller Zeit auf L2 (L1£p L2, wenn es einen in polynomieller Zeit berechenbaren Algorithmus (Turingmaschine, ...) f gibt soda"s: x Î L1 gdw. f(x) Î L2.
NP-Vollst"andigkeit

Definition 14  [NP-vollst"andig]   Ein Problem L aus der Klasse NP ist NP-vollst"andig, falls sich alle Probleme aus der Klasse NP sich polynomiell auf L reduzieren lassen.
Beispiele

15  Schluß und Ausblick

Inhalt R"uckblick ·Was wurde nicht behandelt ·Programmentwicklung
Literatur: Keine spezielle Literatur zum abschlie"senden Kapitel, ein wenig aus [Ski97].
R"uckblick
  1. "Uberblick "uber Datenstrukturen und ihre Algorithmen
    • grundlegende Datenstrukturen
    • Sortieren
    • Graphen und Graphsuche, ...
  2. Grundlegende Probleml"osungsstrategien
    • Divide & Conquer,
    • dyn. Programmierung
    • greedy-Strategien
    • Analyse (bzw. zumindest Absch"atzung) der Laufzeit
Was wurde nicht behandelt?
Programmentwicklung im weiteren Rahmen

References

[AHU89]
Alfred Aho, John E. Hopcroft, and Jeffrey Ullman. The Design and Analysis of Computer Algorithms. Addison-Wesley, 1989.

[AVL62]
G. M. Adel'son-Vel'skivi and E. M. Landis. An algorithm for the organization of information. Soviet Mathematics Doklady, 3:1259--1263, 1962.

[Bay72]
R. Bayer. Symmetric binary B-trees: Data structure and maintenance algorithms. Acta Informatica, 1:290--306, 1972.

[CLR90]
Thomas H. Cormen, Charles E. Leierson, and Ronald L. Rivest. An Introduction to Algorithms. MIT Press, 1990.

[dPF02]
Leonardo di Pisa (``Fibonacci''). Liber Abaci. 1202.

[Heu97]
Volker Heun. Grundlegende Algorithmen. Vorlesungsskript TU München, October 1997.

[HJ89]
C. A. R. Hoare and Cliff B. Jones, editors. Essays in Computing Science. International Series in Computer Science. Prentice Hall, 1989.

[Hoa62]
C. A. R. Hoare. Quicksort. BCS Computer Journal, 5(1):10--15, 1962. Reprinted in [HJ89].

[HU79]
J.E. Hopcroft and J.D. Ullman. Introduction to Automata Theory, Languages, and Computation. Addison-Wesley, 1979.

[Knu73a]
Donald Knuth. Fundamental Algorithms, volume 1 of The Art of Computer Programming. Addison-Wesley, Reading, Massachusetts, 1973.

[Knu73b]
Donald Knuth. Sorting and Searching, volume 3 of The Art of Computer Programming. Addison-Wesley, Reading, Massachusetts, 1973.

[Moo59]
Edward F. Moore. The shortest path through a maze. In Proceedings of the International Symposium on the Theory of Switching, pages 285--292. Harvard University Press, 1959.

[Pap94]
Christos H. Papadimitriou. Computational Complexity. Addison-Wesley, 1994.

[Sed90]
Robert Sedgewick. Algorithms in C. Addison-Wesley, 1990.

[Ski97]
Steven S. Skiena. The Algorithm Design Manual. Springer, Telos, 1997.

[Tar72]
Robert Tarjan. Depth-first search and linear graph algorithms. SIAM Journal on Computing, 1(2):146--160, June 1972.

[Wil64]
J. W. J. Williams. Heapsort (Algorithm 232). Communications of the ACM, 7(6):347--348, 1964.

1
eventuell leer
2
nicht zu verwechseln mit voll: entweder Blatt oder genau zwei geordnete Kinder. Vollständig heißt, es gibt keine teilgefüllten ``Ebenen'' des Baumes. Falls nicht die passende Anzahl von elementen vorganden sind, kann man sich die Letzte Reihe mit leeren Knoten aufgefüllt denken, insofern ist auch die letzte Ebene gef"ullt.
3
'a Btree = Leaf | Node of ('a Btree * 'a * 'a Btree) oder, genauer auch 'a Btree = Leaf of 'a | Node of ('a Btree * 'a * 'a Btree), was mehr [CLR90] entspricht
4
Länge des Arrays l = 2n -1 mit 2n - 1 ³ heapsize für minimales n. Da die letzte Ebene teilgefüllt sein kann ist streng genommen der Baum nicht mehr vollständig, zumindest auf den ersten Blick. Nach der Sichtweise von [CLR90], Ist ein Knoten mit Verweisen auf 2 Kindern als Nil-Pointer kein Blatt, sondern hat zwei leere Knoten als Kindern, die dann die Blätter darstellen. Die Sprechweise in [CLR90] ist auf jedenfall nicht 100% konsistent, denn wenn jeder Knoten auf jeden fall noch 2 nil-Kinder hat, dann ist wiederum der Baum nicht vollkommen balanciert.
5
es gibt daneben Durchlaufen in Präordnung und Postordnung.
6
Beachte: die rekursive L"osung ist endrekursiv.
7
Wenn die Schlüssel alle unterschiedlich sind, gilt für die beiden ersten Ungleichungen schärfer < anstelle £. Die erste Ungleichung gilt nur falls das linke Kind existiert.
8
einfacher Pfad: alle Knoten (des Pfades) unterschiedlich. Falls die Pfade nur von der Eltern zu den Kindern erlaubt sind, sind alle Pfade einfach!
9
= Länge des kürzesten Pfades von s zum Knoten, falls unerreichbar, Abstand = ¥.
10
Daß man 2 Farben braucht, sollte klar sein, die dritte kennzeichnet Knoten die man bereits entdeckt hat, deren adjazierene Knoten man aber noch nicht kennt. Wg. Breitenstrategie arbeitet man die Nachbarn eines neuentdeckten Knotens nicht sofort ab. Das sind die grauen Knoten.
11
graue Knoten: auf der ``Grenze'' der Breitensuche: ein schwarzer Knoten kann nie einem weißen benachbart sein.
12
Queue-Operationen enqueue und dequeue kosten O(1), siehe Kapitel 5.
13
der vorgestellte Algorithmus wendet dies auf alle Knoten der Reihe nach an, das ist aber nicht zentral. Die eigentliche Tiefensuche ist DFS-Visit.
14
Ein weißer Pfad ist ein Pfad, der nur weiße Knoten enthält.
15
sonst wäre v ein Vorfahre; damit h"atten wir eine Rückwärtskante, was der Annahme der Kreisfreiheit widerspricht.
16
Sei G = (V, E) gegeben, dann ist Gt = (V, Et) wobei (v, u) Î Et, gdw. (u,v) Î E. Die starken Zusammenhangskomponenten von G und Gt stimmen überein.
17
jeder von jedem erreichbar. Zum Vergleich mit den starken Zusammenhangskomponenten: der Graph ist hier ungerichtet!
18
Es kann natürlich mehrere leichte/minimale Kanten geben.
19
unter der Voraussetzung, daß a(E,V) = O(logE), z.B., heap-Implementierung.
20
genauer: alle anderen Bäume des Waldes sind nur Einzelknoten.
21
es kann mehrere Bäume mit kürzesten Pfaden geben. In der Regel will man einen davon.
22
Vergleiche Breitensuche und Vorgängergraph. Der Vorgängergraph des Breitensuchalgorithmus entspricht für den Spezialfall, daß alle Kanten das Gewicht 1 haben, den Baum der kürzesten Pfade.
23
extrahieren kostet O(V) (z.B. Arrayimplementierung), wenn man nicht noch Zusatzannahmen macht.
24
konkrete Beispiele aus der Vorlesung: Problem der k"urzesten Pfade. Speziell f"ur D&C: Quicksort, Mergesort.
25
nach Leonardo von Pisa, ``Sohn des Bonacci'' [dPF02].
26
Das Beispiel wird manchmal mi"sinterpretiert als: ``Fibonacci zeigt, wieviel schlechter rekursive L"osungen gegen"uber iterativen sind''. Das ist hier nicht der Punkt. Man erh"alt eine effiziente rekursive L"osung mittels Parameterakkumulation.
27
Punkt 1 und 2 des Vorgehens bei DP
28
zweifache Schleife, eine f"ur min, eine f"ur die Summe.
29
der Algorithmus l"auft tats"achlich mit nur mit O(k n2), denn bei der Berechnung der Summen åj=i+1n si kann man ebenfalls sparen, in dem man Teilsummen wiederverwendet.
30
Die Wahl der Strategie h"angt nat"urlich auch von der Struktur des Problems ab; wenn man keine speziellen Eigenschaften des Suchraums kennt/ausnutzen will, ist Tiefensuche meist die erste Wahl, Breitensuche eher selten.
31
Anwendbar meint: im Prinzip anwendbar, aber das faktorielle Wachstum macht das Problem ``unbehandelbar'' (intractable).
32
die charakteristische Funktion
33
die Abst"ande/Kosten seien als immer ³ 0 angenommen
34
Es gibt Algorithmen, die Gausselimination in O(b n2) machen, wobei b die Bandbreite ist.
35
man kennt nichtmal Approximationen mit garantierter G"ute. Allerdings gibt es ad-hoc Heuristiken
36
von denen sehr viele ``hart'' sind
37
zumindest komplexit"atsm"a"sig
38
alternativ: gibt es eine Rundreise mit Kosten £ k
Page last (re-)generated May 21, 2003 (Martin Steffen)
This document was translated from LATEX by HEVEA.