2. Wiederholungsblatt zu Algorithmen I im SS 2010

Werbung
Prof. Dr. Peter Sanders
G.V. Batz, C. Schulz, J. Speck
Karlsruher Institut für Technologie
Institut für Theoretische Informatik
2. Wiederholungsblatt zu Algorithmen I im SS 2010
http://algo2.iti.kit.edu/AlgorithmenI.php
{sanders,batz,christian.schulz,speck}@kit.edu
Musterlösungen
Aufgabe 1
(Bellman-Ford, 4 Zusatzpunkte)
Bestimmen Sie für folgenden Graphen den Baum der kürzesten Wege mit Startknoten s, und bestimmen Sie die Distanzen aller Knoten von s. Tragen Sie das Endergebnis in die Zeichnung ein.
−1
4
−2
−2
3
−2
3
6
5
−1
6
−1
4
1
7
s
Musterlösung:
−1
4
5
−2
−2
−2 3
3
3
6
6
5
−1
3
−1
1
7
6
4
1
4
0
s
Aufgabe 2
(Schnelles Potenzieren, 4 Zusatzpunkte)
Gegeben ist der Algorithmus power(a : R; n0 : N) aus der Vorlesung (siehe Folie 51 der Vorlesungsfolien). Führen Sie den Algorithmus für a = 2, n0 = 15 aus und geben Sie die Belegung der Variablen
p, r, n zu Beginn des Programms und jeweils am Ende der Whileschleife an. Nutzen Sie dafür die
folgende Tabelle!
Durchlauf
Start
1
2
3
4
5
6
7
n
r
p
Musterlösung:
Durchlauf
Start
1
2
3
4
5
6
7
Aufgabe 3
n
15
14
7
6
3
2
1
0
r
1
2
2
8
8
128
128
32768
p
2
2
4
4
16
16
256
256
(Anwendungsproblem)
Ein Staat möchte seine Internet-Infrastruktur erneuern. Es gibt n Städte die alle angebunden werden
n×n
insollen, wobei indirekte Verbindungen ausreichen. Eine symmetrische Matrix C = ((cij )) ∈ R>0
formiert darüber, wieviel Einheiten der einheimischen Währung es jeweils kosten würde, eine Leitung
zwischen je zwei Städten zu bauen.
a) Entwickeln Sie einen Algorithmus, der eine kostenminimale Netzinfrastruktur berechnet,
die alle
n Städte direkt oder indirekt anbindet. Ihr Algorithmus darf höchsten O n2 log n Zeit brauchen.
b) Begründen Sie kurz, warum Ihr Algorithmus das gewünschte Laufzeitverhalten aufweist.
Musterlösung:
a) Eine optimal kostengünstige Netzinfrastruktur entspricht einem MST im gewichteten vollständigen ungerichten Graphen mit Knotenmenge {1, . . . , n}, wobei eine Kante {i, j} jeweils das Gewicht cij hat. Dazu interpretiere man die Matrix C als Adjazenzmatrix mit zusätzlicher Gewichtsinformation. Aus dieser erzeuge man einen Folge von gewichteten Kanten h. . . , ({i, j}, cij ), . . . i
mit 1 ≤ i < j ≤ n und wende darauf Kruskals Algorithmus an (was sortieren der Kantenfolge
beinhaltet).
b) Das Erzeugen der Folge von Kanten braucht O n2 Zeit, die erzeugte Folge hat n(n − 1)/2 ≤ n2
Elemente. Für
sortieren
O n2 log n2 =
diese 2Folge benötigt Kruskals Algorithmus inklusive
2
2
O n · 2 log n = O n log n Zeit. Insgesamt braucht man also O n log n Zeit.
2
Aufgabe 4
(MST )
Gegeben sei ein ungerichteter, zusammenhängender Graph ohne Mehrfachkanten und Schleifen. Die
Kanten seien gewichtet und o. B. d. A. nach Gewicht sortiert: w(e1 ) ≤ w(e2 ) ≤ . . . ≤ w(em ). Zeigen
Sie, dass e2 in diesem Fall immer Teil eines minimalen Spannbaums sein kann.
Musterlösung:
Wir geben drei mögliche Lösungen an:
a) Da der Graph eine zweitleichteste Kante besitzt, hat er auch mindestens zwei Kanten. Also gibt
es auch mindestens drei Knoten im Graphen, sonst hätten wir schon mindestens eine Mehrfachkante. Ein minimaler Spannbaum enthält damit auch mindestens zwei Kanten. Führt man den
Algorithmus von Kruskal aus, so kann e2 an zweiter Stelle gewählt werden. Die ersten beiden
Kanten werden auf jeden Fall zum minimalen Spannbaum hinzugenommen, da man mit einer
bzw. zwei Kanten noch keinen Zyklus bilden kann (Argument wiederum: keine Mehrfachkanten).
Außerdem arbeitet Kruskals Algorithmus gierig, nimmt also niemals eine Kante wieder aus dem
Spannbaum heraus.
b) Sei T ein minimaler Spannbaum des Graphen, und e eine beliebige Kante mit Gewicht w(e2 ).
Falls e ∈ T , sind wir fertig. Andernfalls hat T ∪ {e} genau einen Zyklus C mit mindestens
drei Kanten, also außer e mindestens zwei andere. Eine dieser beiden anderen, nämlich ex , hat
mindestens Gewicht w(e2 ). Diese Kante ex entfernen wir. T ∪ {ex } \ {e} ist nun wieder ein
Spannbaum und auch minimal, denn w(ex ) ≥ w(e). Tatsächlich gilt sogar w(ex ) = w(e2 ).
c) Sei e1 = (u, v) und e2 = (w, x). Einer der beiden Knoten von e2 ist nicht zu e1 inzident, also
u 6= w 6= v oder u 6= x 6= v, sonst hätten wir eine Mehrfachkante. Dieser Knoten sei o. B. d. A. x.
Definiere den Schnitt S = {u, v, x}. e2 führt aus S heraus und ist außerdem eine der leichtesten
herausführenden Kanten, denn nur e1 kann noch leichter sein, führt aber nicht aus S heraus.
Somit kann e2 nach der Cut Property in einem minimalen Spannbaum verwendet werden.
Aufgabe 5
(Durchmesser eines Graphen)
Skizzieren Sie einen Algorithmus, der den Durchmesser eines ungerichteten, zusammenhängenden Graphen G = (V, E) berechnet und dabei höchstens O(nm) Zeit benötigt (n := |V |, m := |E|). Sie können
dazu einen bekannten Algorithmus modifizieren und verwenden. Begründen Sie, warum Ihr Algorithmus das gewünschte Laufzeitverhalten hat.
Anmerkung: Der Durchmesser D eines Graphen G ist der maximale Abstand zwischen zwei Knoten
in G. Der Abstand zweier Knoten sei dabei die minimale Kantenanzahl eines Pfades zwischen diesen
Knoten in G.
Mathematisch: D := maxu,v∈V minP ∈P(u,v) |P |, wobei P(u, v) die Menge aller Pfade zwischen u und
v ist, und |P | die Anzahl der Kanten in Pfad P .
Musterlösung: Zur Lösung des Problems modifiziere man die Breitensuche derart, dass sie für
einen gegebenen Startknoten s ∈ V den maximalen Abstand von s zu einem Knoten v ∈ V liefert,
also einfach die höchste vergebene BFS-Nummer. Wir erhalten also eine Funktion
bfs(s : NodeID) : N0 .
Dann berechnet der folgende Algorithmus den Durchmesser von G:
function durchmesser (V : Set of NodeID, E : Set of unordered Pair of NodeID) : N0
res := h := 0 : N0
3: for each v ∈ V do
4:
h := bfs(v)
1:
2:
3
if h > res then res := h
6: end for
7: return res
5:
// n Aufrufe von bfs
Die Funktion bfs benötigt O(m + n) Zeit, da bei einer Ausführung von bfs jede Kante O(1)-mal angefasst wird und für jeden Knoten genau einmal der Abstand ermittelt wird. Weil G zusammenhängend
ist, gilt aber m ≥ n − 1. D.h. bfs braucht O(m) Zeit, insgesamt also O(nm) Zeit.
Aufgabe 6
(Hashing)
Gegeben sei eine leere Hash-Tabelle mit linearem Suchen ( open hashing with linear probing“). Als
”
Hashfunktion werde h(x) := x mod 10 verwendet.
a) Nun werde die Operationsfolge
insert(13), insert(16), insert(4), insert(14), insert(3)
ausgeführt. In welchen Zustand befindet sich die Hashtabelle nach der Ausführung? Tragen Sie
die Lösung in die folgende Tabelle ein:
0
1
2
3
4
5
6
7
8
9
b) Gegeben sei nun eine weitere Hash-Tabelle mit linearem Suchen ( open hashing with linear
”
probing“). Die Operation remove sei dabei so implementiert, dass keine Löschmarkierungen
gesetzt werden (verwenden Sie den Algorithmus aus der Vorlesung). Als Hashfunktion werde
wieder h(x) := x mod 10 verwendet.
Die Hashtabelle befinde sich in dem wie folgt skizzierten Zustand:
0
1
2
3
4
5
6
7
8
9
21
1
12
14
2
13
Nun werde die Operation remove(12) ausgeführt. In welchen Zustand befindet sich die Hashtabelle nach der Ausführung? Tragen Sie die Lösung in die folgende Tabelle ein:
4
0
1
2
3
4
5
6
7
8
9
Musterlösung:
a)
b)
Aufgabe 7
0
1
0
1
21
2
2
1
3
13
4
4
3
2
4
14
5
14
5
13
6
16
7
3
8
9
6
7
8
9
(Mehrfach-Selektion)
Gegeben sei eine (unsortierte) Menge M von n Elementen sowie eine totale Ordnungsrelation < auf
M (die Ordnungsrelation sei in konstanter Zeit auswertbar). Skizzieren Sie einen vergleichsbasierten
Algorithmus, der die Elemente mit den Rängen1 1, 2, 4, 8, . . ., 2blog2 nc berechnet und dafür im
schlimmsten Fall O(n) Zeit benötigt.
Dabei dürfen Sie eine Funktion select(M, k) verwenden, die das Element mit Rang k einer Menge M
zurückgibt, und zwar im schlechtesten Fall in O(|M |) Zeit.
Begründen Sie kurz, wieso Ihr Algorithmus das gewünschte Laufzeitverhalten aufweist.
Musterlösung:
1: function logselect(M )
2: if |M | = 0 then
3:
return hi
4: end if
5: ` = 2blog2 |M |c
6: p = select(M , `)
7: M< := {a ∈ M : a < p}
8: return logselect(M< ) ◦hpi
Obige Prozedur gibt die gewünschten Elemente in aufsteigender Reihenfolge aus. Zeile 7 benötigt
lineare Zeit. Nach dem ersten Schritt halbiert sich |M | bei jeder Rekursion, so dass sich mittels
geometrischer Reihe (oder Master-Theorem) eine insgesamt lineare Laufzeit ergibt.
Aufgabe 8
(Prioritätswarteschlangen)
a) Beschreiben Sie, wie man eine adressierbare Prioritätswarteschlange mittels einer doppelt verketteten Liste implementiert. Ein Listenglied repräsentiere dabei ein Element in der Schlange,
ein Handle sei ein Handle eines Listenglieds. Es gelte die Invariante, dass die Liste vor und nach
jeder Operation sortiert ist. Skizzieren Sie alle Operationen der Schnittstelle und geben Sie die
asymptotischen Laufzeiten an.
1
Für eine endliche totalgeordnete Menge (M, <) hat ein Element x ∈ M genau dann den Rang i ∈ N, wenn m1 <
m2 < · · · < x = mi < · · · < mn gilt für M = {m1 , . . . , mn }.
5
b) Beschreiben Sie, wie man eine adressierbare Prioritätswarteschlange mittels einer sortierten Folge
(z. B. (a, b)-Baum) implementiert. Skizzieren Sie alle Operationen der Schnittstelle und geben
Sie die asymptotischen Laufzeiten an.
Musterlösung:
a) Die Liste wird immer in aufsteigender Reihenfolge sortiert gehalten.
Konstruktion: Lege leere Liste an. Laufzeit O(1).
insert(e): Füge e nach linearer Suche an der richtigen Stelle in die Liste ein, gebe Handle auf
Listenglied zurück. Laufzeit O(n).
deleteMin(): Gib erstes Element zurück und lösche das Listenglied. Laufzeit O(1).
decreaseKey(h, k): Ändere Schlüssel bei h und schiebe es an die richtige Stelle. Laufzeit O(n).
optional:
remove(h): Lösche Listenglied h. Laufzeit O(1).
merge: Mische beide Listen in üblicher Weise. Laufzeit O(n1 + n2 ).
b) Für die Folge wählen wir eine aufsteigende Reihenfolge.
Konstruktion: Lege leere sortierte Folge an. Laufzeit O(1).
insert(e): Füge e in sortierte Folge ein, gebe Handle auf Folgenglied zurück. Laufzeit O(log n).
deleteMin(): Gib erstes Element zurück und lösche das Folgenglied. Laufzeit O(log n).
decreaseKey(h, k): Entferne h und füge es mit neuem Schlüssel wieder ein. Laufzeit O(log n),
noch besseres Verhalten möglich mit Finger-Suche.
optional:
remove(h): Lösche Folgenglied h. Laufzeit O(log n).
Aufgabe 9
(O-Notation)
a) Zeigen Sie: g(n) = O(f (n)) =⇒ O(f (n) + g(n)) = O(f (n)).
b) Zeigen Sie: O(f (n)) · O(g(n)) = O(f (n) · g(n)).
c) Für o(f (n)) wird auch die folgende, alternative Definition verwendet:
g(n) = o(f (n))
:⇐⇒
lim |g(n)/f (n)| = 0 .
n→∞
Es soll nun bewiesen werden, dass beide Definitionen äquivalent sind für g : N≥0 → R≥0 und
f : N≥0 → R>0 . Zeigen Sie also:
g(n)
=0
n→∞ f (n)
lim
⇐⇒
∀c ∈ R>0 : ∃n0 ∈ N>0 : ∀n ≥ n0 : g(n) ≤ cf (n)
Musterlösung:
a) Nach Vorraussetzung ist g(n) ∈ O(f (n)). Nach Definition gibt es also c ∈ R>0 und n0 ∈ N, so
dass für alle n ≥ n0 gilt: g(n) ≤ cf (n). Es ist also
f (n) + g(n) ≤ f (n) + cf (n) = (1 + c)f (n)
für alle n ≥ n0 . Damit gilt die Behauptung.
6
b) Zunächst: O(f (n)) · O(g(n)) bezeichnet eine Menge von Funktionen, nämlich
n
o
h0 (n) · h00 (n) h0 (n) ∈ O(f (n)) und h00 (n) ∈ O(g(n)) .
Des weiteren bezeichnet das Gleichheitszeichen hier tatsächlich eine Gleichheit (von Mengen).
Es handelt sich in diesem Fall also nicht um einen Ausdruck der informellen Schreibweise, wie
sie sich im Rahmen der O-Notation eingebürgert hat.
⊆ Sei also h(n) ∈ O(f (n)) · O(g(n)). Dann exisitieren h0 (n) ∈ O(f (n)) und h00 (n) ∈ O(g(n))
mit h(n) = h0 (n) · h00 (n). Weiter gibt es c0 , c00 ∈ R>0 und n00 , n000 ∈ N mit
h(n) = h0 (n) · h00 (n) ≤ c0 f (n) · c00 g(n) = c0 c00 (f (n) · g(n))
für alle n ≥ max{n00 , n000 }. Also ist h(n) ∈ O(f (n) · g(n)).
⊇ Sei umgekehrt h(n) ∈ O(f (n) · g(n)), d.h. es gibt c ∈ R>0 , n0 ∈ N0 mit h(n) ≤ c · f (n)g(n)
für alle n ≥ n0 . Somit gilt
h(n)
≤ cf (n) für alle n ≥ n0
g(n)
=⇒
h(n)
∈ O(f (n)) ,
g(n)
zumindest wenn g(n) > 0 für alle n ≥ n0 . In diesem Fall erhalten wir also
h(n) =
h(n)
· g(n) ∈ O(f (n)) · O(g(n)) .
|{z}
g(n)
| {z } ∈O(g(n))
∈O(f (n))
Was passiert aber falls g(n) doch Nullstellen jenseits von n0 besitzt? Sei also n1 ≥ n0 mit
g(n1 ) = 0. Dann ist h(n1 ) ≤ c · f (n1 )g(n1 ) = 0. Da h(n) nach Definition der O-Notation aber
nie negativ wird, gilt sogar h(n1 ) = 0. Insgeasamt ist also h(n) = h0 (n) · g(n) mit
(
h(n)
0
g(n) für alle n mit g(n) 6= 0 .
h (n) =
0
sonst
Es ist aber h0 (n) ∈ O(f (n)). Also gilt h(n) = h0 (n) · g(n) ∈ O(f (n)) · O(g(n)).
c) Der Grenzwert ist folgendermaßen definiert: Sei f : D → R ein Funktion mit D ⊆ R und D nach
oben unbeschränkt. Dann gelte
lim f (x) = a
x→∞
:⇐⇒
∀ε > 0 : ∃x0 ∈ R : ∀x ≥ x0 : |f (x) − a| ≤ ε
Diese Definition erinnert in ihrer äußeren Form schon an die Definition von o(. . . ). In der Tat
ist für den Beweis auch gar nicht mehr viel zu tun:
Seien also g : N0 → R≥0 und f : N0 → R+ Funktionen. Dann gilt folgende Äquivalenzumformung:
g(n) g(n)
nach Def
≤ε
=0
⇐⇒
∀ε > 0 : ∃x0 ∈ R : ∀n ≥ x0 : lim
n→∞ f (n)
f (n) g(n),f (n)≥0
⇐⇒
Aufgabe 10
∀ε > 0 : ∃n0 ∈ N : ∀n ≥ n0 : g(n) ≤ εf (n)
(Geldwechselproblem und dynamisches Programmieren)
Angenommen, Sie haben die Aufgabe, eine Wechselgeldrückgabe für Getränkeautomaten zu programmieren. Ziel ist es dabei, immer die optimale (d. h. kleinstmögliche) Anzahl von Münzen zurückzugeben. Für viele Münzwertsysteme (z. B. auch für den Euro) wird diese Aufgabe durch das GreedyVerfahren in Abbildung 1 optimal gelöst.
7
a) Im Fall des Euro ist die Menge der Münzwerte M = {1, 2, 5, 10, 20, 50, 100, 200} (alle Werte in
Cent). Zeigen Sie: Der Greedy-Algorithmus aus Abbildung 1 liefert nicht immer ein optimales
Ergebnis, wenn eine zusätzliche Münze im Wert von 4 Cent eingeführt wird.
Nun soll mit Hilfe von dynamischem Programmieren eine Algorithmus entwickelt werden, der für
jedes beliebige Münzsystem M mit 1 ∈ M stets eine optimale Lösung liefert (wäre 1 6∈ M , so könnten
manche Beträge nicht dargestellt werden).
b) Identifizieren Sie die optimalen Teillösungen einer optimalen Lösung. Die Optimalität dieser
Teillösungen muss dabei bewiesen werden. Was sind die trivialen Teilprobleme und deren trivialoptimale Lösungen?
c) Geben Sie die optimale Lösung als Rekurrenz an.
d) Geben Sie den fertigen Algorithmus in Pseudo-Code an.
1:
2:
3:
4:
5:
6:
7:
procedure Geldrueckgabe(rueckbetrag : N0 , M : Set of N)
rest := rueckbetrag : N0
while rest > 0 do
wähle m ∈ M sodass m maximal ist mit m ≤ rest
print Gib eine Münze im Wert von m zurück.
rest := rest − m
end while
Abbildung 1: Ein Greedy-Verfahren zur Bestimmung der verwendeten Münzen bei der Geldrückgabe.
Alle Werte sind natürliche Zahlen und werden z. B. in Cent angegeben. Die Menge M umfasst die
möglichen Münzwerte.
Musterlösung:
a) Der Beweis erfolgt durch ein Gegenbeispiel: Man betrachte den Rückgabebetrag 8 Cent. In diesem
Fall empfiehlt der Algorithmus die Rückgabe der Münzwerte {5, 2, 1}. Die optimale Lösung ist
jedoch {4, 4} (wir verwenden Multimengen).
b) Probleme, Teilprobleme, Lösungen und Teillösungen. Das zu lösende Problem ist durch
den Betrag b gegeben, ein Teilproblem ist ein Teilbetrag b0 ≤ b. Eine Lösung zum Betrag b wird,
wie schon
in Teilaufgabe a), durch ein Multimenge L von Münzwerten aus M ausgedrückt, wobei
P
ist eine Teilmultimenge L0 ⊆ L. Dabei löst L0
b = m∈L m gelten muss. Eine Teillösung von L
P
das Teilproblem, das durch den Teilbetrag b0 = m∈L0 m gegeben ist. Eine optimale Lösung ist
eine Lösung L mit einer minimalen Anzahl von Elementen.
Beweis der Optimalität von Teillösungen einer optimalen Lösung. Eine Teillösung
L0 ⊆ L einer optimalen Lösung L ist eine optimale Lösung des zu L0 gehörenden Teilproblems.
Wäre eine Teillösung L0 nämlich nicht optimal, so gäbe es eine bessere P
Lösung für das
von L0
P
gelöste Teilproblem, also eine Teilmultimenge L00 ⊆ L mit |L00 | < |L0 | und m∈L00 m = m∈L0 m.
Dann wäre aber L̃ := (L\L0 ) ∪ L00 eine bessere Lösung des von L gelösten Gesamtproblems.
Widerspruch zur Optimalität von L.
Triviale Probleme und triviale Lösungen. Das einzige triviale Problem ist durch den Betrag 0 gegeben, die zugehörige trivial-optimale Lösung ist die leere Multimenge ∅.
c) Eine optimale Lösung für einen Betrag b ∈ N0 und eine endliche Menge von Münzwerten M
wird durch die Rekurrenz
minMultSet L(b − m) ∪ {m} m ∈ M ∧ m ≤ b falls b > 0
L(b) =
∅
sonst
8
gegeben (wobei minMultSet aus einer Kollektion von Multimengen eine Multimenge minimaler
Größe auswählt). Man beachte, dass man in einem Rekursionschritt nicht alle Teilbeträge von b
betrachten muss, sondern nur die, die um jeweils einen Münzbetrag kleiner sind.
d) Die Rekurrenz aus Teilaufgabe c) kann mehr oder weniger direkt in einen Algorithmus überführt
werden. Allerdings arbeitet unser Algorithmus nicht mehr rekursiv. Die Teillösungen werden
vielmehr bottom-up“ mit Hilfe einer Tabelle berechnet. Außerdem verwalten wir keine Mul”
timengen. Es reicht uns stattdessen, nur den Münzwert zu speichern, der zu nächstkleineren
Teillösung hinzukommt.
procedure GeldrueckgabeOptimal (b : N0 , M : Set of N)
assert(1 ∈ M )
// Andernfalls gibt es für manche Beträge keine Lösung
3: k : Array[0..b] of {0, . . . , b}
// Minimale Anz. von Münzen für jedes Teilproblem
4: coin := h0, b, . . . , bi : Array[0..b] of {0, . . . , b}
// Hinzukommende Münze zum
5:
// nächst kleineren Teilproblem i − coin[i]
1:
2:
6:
7:
8:
9:
10:
11:
12:
13:
// Lösung bottom-up konstruieren
for i = 1 to b
for each d ∈ M
if d ≤ i then
if k[i − d] + 1 < k[i] then
k[i] := k[i − d] + 1
coin[i] := d
// Differenz zur nächst kleineren Teillösung i − coin[i]
14:
15:
16:
17:
18:
19:
20:
// Lösung ausgeben
i := b
while i > 0 do
print coin[i]
i := i − coin[i]
end while
Man beachte, dass die nächst kleinere Teillösung für einen Betrag i nicht unbedingt die Lösung
für den Teilbetrag i − 1 ist, sondern die Lösung für den Teilbetrag i − coin[i]. Dies liegt daran,
dass zu dieser Teillösung ja gerade der Betrag coin[i] hinzugekommen ist.
Aufgabe 11
(Listen)
In unserer einfachen Listen-Datenstruktur ist die Bestimmung der Listenlänge in konstanter Zeit nicht
möglich. Das kann durch die Einführung einer Objektvariable size behoben werden, die aktualisiert
wird, sobald sich die Anzahl der Elemente verändert. Operationen, die mehrere Listen betreffen,
müssen nun über diese Listen Bescheid wissen, obwohl Low-Level-Funktionen wie splice nur Handles
auf die betreffenden Elemente benötigen. Der Code, um ein Element a aus Liste L in Liste L0 nach a0
zu verschieben, wäre beispielsweise:
1: procedure moveAfter (a, a0 : Handle; L, L0 : List)
2: splice(a, a, a0 )
3: L.size – –
4: L0 .size++
Modifizieren Sie die Operationen remove, insertAfter und concat so, dass size korrekt aktualisiert
wird.
Musterlösung:
1: procedure remove(b : Handle; L : List)
2: moveAfter(b, freeList.head, L, freeList)
9
1:
2:
3:
4:
5:
6:
procedure insertAfter (x : Element; a : Handle; L : List)
checkFreeList
a0 := freeList.first
moveAfter(a0 ,a,freeList, L)
a0 → e := x
return a0
procedure concat(L, L0 : List)
2: splice(L0 .first, L0 .last, L.last)
3: L.size :=L.size + L0 .size
4: L0 .size :=0
1:
O-Phase 2010
TUTOREN
GESUCHT
!!! Werde selbst O-Phasen-Tutor !!!
Seminar vom 4. bis 6. Oktober, Tutorentag am 9. Oktober, O-Phase vom 11. bis 16. Oktober
Weitere Infos über die Fachschaft Mathe/Info
Ausgabe: Montag, 5.7.2010
Abgabe (nur für Zusatzpunkte): Freitag, 9.7.2010, 12:45 im Briefkasten im UG, Gebäude 50.34
10
Herunterladen