Algorithmen & Datenstrukturen
Wintersemester 2000/2001

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]
     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
      do 
         heapify(A,i);





Heapsort






Heapsort(A)

   build_heap(A);
   for i = length(A) downto 2
   do  exchange A[1] A[i];
       heapsize[A] := heapsize[A] - 1;
       Heapify(A,1);



Quicksort


Quicksort (2)




(* $Id: quicksort.ml,v 1.6 1999/11/11 10:43:28 ms Exp $ *)

open Basic;;
open Sort;;

module QuicksortFun (E: ORDERED) : (SORT with type elem = E.t) =
    struct
      type elem = E.t 
     
      let rec filter p l = match l with 
     []     -> []
      | 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  -> 
   sort (filter (function y -> E.lt(y,x)) tl)
   @ [x] @                                     (* Pivot *)
   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                          /* Invariante I_2 */
  do     repeat j := j-1
           until A[j] <= x;
         repeat i := i+1
           until A[i] >= x;
         if   i < j                   /* Invariante I_1 */
         then exchange A[i] <-> A[j]  /* Invariante 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 log n) O(n log n) O(nlog n)
direktes Mischen O(n log n) O(n log n) O(nlog n)
natürliches Mischen O(n) O(n log n) O(nlog n)
Quicksort O(nlog n) O(n2) O(nlog n)
Heapsort O(n log n)

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 lg n).

Abschätzung der Laufzeit(2)
Beweis von 1:



Lineares Sortieren: Counting Sort
Counting Sort




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

  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(nlog n), 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
)
  =
a
logb n
 
T æ
ç
ç
ç
è
n
b
logb n
 
ö
÷
÷
÷
ø
+
(logb n)-1
å
i=0
ai f(
n
bi
)
  =
a
logb n
 
T(1) +
(logb n)-1
å
i=0
ai f(
n
bi
)
  =
d n
logb a
 
+
(logb n)-1
å
i=0
ai f(
n
bi
)
Spezialfall: lineare Zeitkomplexität


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



 
  £ O(n)

Spezialfall: O(nlog n)




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



 
  =
d n + c n
(logb n)-1
å
i=0
1
  £ O(nlog n)
Spezialfall: polynomiale Laufzeit
T(n) =
d n
logb a
 
+
(logb n)-1
å
i=0
c æ
ç
ç
è
a
b
ö
÷
÷
ø
i



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



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



 
ö
÷
÷
÷
÷
÷
ø
  £
O æ
ç
ç
ç
è
n
logb a
 
+ n
a
logb n
 
b
logb n
 
ö
÷
÷
÷
ø
  £
O æ
è
n
logb 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
Einfügen O(1)
Löschen (bei geg. Element) O(1)
Suchen O(n)

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 prv(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 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



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);  // zwei Hilfsvariablen
  while  x != nil          // finde das passende Blatt
  do
    y := x;                // y zum merken des Vaters
    if   key(z) < key(x)
    then x := left(x) 
    else x := right(x)
  od;                     
  p(z) := y;               // => x ist nil, y sein Vorgaenger
  if   y = nil             // Sonderfall: y = Wurzel;
  then root(T) := z
  else if   key(z) < key(y)// f"uge an der passenden Stelle
       then  left(y) := z  // z als neues Blatt ein.
       else right(y) := z 



Baumoperationen: Löschen




Delete(T,z)           // T = tree, z = zu loeschender Knoten
  if   left(z) = nil or right(z) = nil
    then y := z              // y tritt an die Stelle von x
    else y := Successor(z);  // wobei: y hat <= 1 Kind!

  if left(y) != nil          // Bestimmung des Kindes x
     then x :=  left(y)      // von y.
     else x := right(y);
                                 // Ver"andern der Verzeigerung
  if x != nil then p(x) = p(y);  // y herausgenommen => x mit 
                                 // neuem Vater
  if p(y) = nil                  // Spezialfall: wurzel
    then root(t) = x
    else if  y = left(p(y))
           then  left(p(y)) := x
           else right(p(y)) := x  // y ist nun  vom
                                  //  urspr. Platz entfernt

  if y != z then key(z) := key(y);// z durch y ersetzen

  return y;



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:
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;
 
       left(y):= x                // x != nil
       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. 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.
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.12

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[u] < f[v]. Sei (u,v) eine Kante. Wenn sie mit DFS erkundet wird, ist v nicht Grau (sonst wäre v ein Vorfahre Þ Rückwärtskante) Þ 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 Vorgänger 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 von Vorvater.


Analyse (2)


10   Spannbäume

Literatur: Kapitel 24 aus [CLR90].
Inhalt Kantengewichte ·minimale Spannbäume·greedy-Strategien ·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.15
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"urzeste Pfade

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


Definition 11  [K"urzester 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"urzesten 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"urzester Pfad von u nach v ist ein Pfad von u nach v mit w(p) = d(u,v).

Problemvarianten
K"urzeste Pfade
K"urzeste Pfade (2)


Hauptidee: Der k"urzeste Pfad zwischen zwei Knoten
enth"alt k"urzeste Pfade als Teile
Lemma 7   geg G = (V, E), gerichtet, gewichtet mit w: E ® R. (v1,..., vn) ist ein k"urzester Pfad von v1 nach vn und w = (vi,..., vj) mit 1£ i £ j £ n ein Teilpfad. Dann ist w ein k"urzester 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;





Lineare Programmierung


Darstellung als Graphproblem


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.

[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].

[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.

[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
('a Btree = Leaf | Node of ('a Btree * 'a * 'a Btree)
3
Länge des Arrays l = 2n -1 mit 2n - 1 ³ heapsize für minimales n.
4
es gibt daneben Durchlaufen in Präordnung und Postordnung.
5
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.
6
einfacher Pfad: alle Knoten (des Pfades) unterschiedlich. Falls die Pfade nur von der Eltern zu den Kindern erlaubt sind, sind alle Pfade einfach!
7
= Länge des kürzesten Pfades von s zum Knoten, falls unerreichbar, Abstand = ¥.
8
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.
9
graue Knoten: auf der ``Grenze'' der Breitensuche: ein schwarzer Knoten kann nie einem weißen benachbart sein.
10
Queue-Operationen enqueue und dequeue kosten O(1), siehe Kapitel 5.
11
der vorgestellte Algorithmus wendet dies auf alle Knoten der Reihe nach an, das ist aber nicht zentral. Die eigentliche Tiefensuche ist DFS-Visit.
12
Ein weißer Pfad ist ein Pfad, der nur weiße Knoten enthält.
13
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.
14
jeder von jedem erreichbar. Zum Vergleich mit den starken Zusammenhangskomponenten: der Graph ist hier ungerichtet!
15
Es kann natürlich mehrere leichte/minimale Kanten geben.
16
unter der Voraussetzung, daß a(E,V) = O(log E), z.B., heap-Implementierung.
17
genauer: alle anderen Bäume des Waldes sind nur Einzelknoten.
18
es kann mehrere B"aume mit k"urzesten Pfaden geben. In der Regel will man einen davon.
19
Vergleiche Breitensuche und Vorg"angergraph. Der Vorg"angergraph des Breitensuchalgorithmus entspricht f"ur den Spezialfall, da"s alle Kanten das Gewicht 1 haben, den Baum der k"urzesten Pfade.
20
extrahieren kostet O(V) (z.B. Arrayimplementierung), wenn man nicht noch Zusatzannahmen macht.
21
jede Reihe der Matrix enth"alt eine 1 und eine -1 und ansonsten nur 0.
January 23, 2001
This document was translated from LATEX by HEVEA.