Algorithmen und Datenstrukturen - IFIS Uni Lübeck

Werbung
Prof. Dr. V. Linnemann
Universität zu Lübeck
Institut für Informationssysteme
Lübeck, den 5. Oktober 2010
Algorithmen und Datenstrukturen
Sommersemester 2010
2. Klausur Lösungen
Hinweis:
• Achten Sie bei Programmieraufgaben darauf, Ihre Lösungen zu kommentieren, so dass
es möglich ist, den Lösungsweg zu verfolgen und die Idee der gewählten Implementierung zu verstehen. Fehlende Kommentare führen zu einem erheblichen Punktabzug
• Insgesamt sind bei dieser Klausur 80 Punkte zu erzielen. Zum Bestehen der Klausur
genügen 50% der Punkte
Lösung 1: Laufzeitstack
Was bewirkt die wasmachich - Methode der folgenden Klasse ? Was bewirkt die main Methode ? Zeichnen Sie den Laufzeitstack jeweils zu Ende eines wasmachich - Aufrufs,
nachdem der Ergebniswert im Stack vermerkt wurde. Verwenden Sie als Rückkehradresse
jeweils den in der Klasse angegebenen Kommentar.
public class WasMachIch {
public static int wasmachich(String wort, char zeichen, int i){
if (i>=wort.length()) return 0;
if (wort.charAt(i)==zeichen)
return wasmachich(wort, zeichen, i+1) /* A */ +1;
else
return wasmachich(wort, zeichen, i+1) /* B */;
}
public static void main (String[] args){
int ergebnis =
wasmachich("BONN",’N’,0) /* C */;
System.out.println(ergebnis);
}
}
(8 Punkte)
1/12
Lösung:
wasmachich liefert als int - Wert, wie oft das Zeichen zeichen in der Zeichenkette wort
ab der Position mit dem Index i vorkommt, d.h. bei i = 0 (1. Aufruf) wird abgeliefert, wie
oft das Zeichen zeichen in der Zeichenkette wort vorkommt. Die main - Methode gibt über
die Konsole aus, wie häufig der Buchstabe N in der Zeichenkette BON N vorkommt, d.h. es
wird die Zahl 2 ausgegeben
Laufzeitstack:
Ergebnis
wort
zeichen
i
Rückkehradresse
Ergebnis
wort
zeichen
i
Rückkehradresse
Ergebnis
wort
zeichen
i
Rückkehradresse
Ergebnis
wort
zeichen
i
Rückkehradresse
Ergebnis
wort
zeichen
i
Rückkehradresse
BONN
N
0
C
BONN
N
1
B
BONN
N
2
B
BONN
N
3
A
0
BONN
N
4
A
Ergebnis
wort
zeichen
i
Rückkehradresse
Ergebnis
wort
zeichen
i
Rückkehradresse
Ergebnis
wort
zeichen
i
Rückkehradresse
Ergebnis
wort
zeichen
i
Rückkehradresse
BONN
N
0
C
BONN
N
1
B
BONN
N
2
B
1
BONN
N
3
A
Ergebnis
wort
zeichen
i
Rückkehradresse
Ergebnis
wort
zeichen
i
Rückkehradresse
Ergebnis
wort
zeichen
i
Rückkehradresse
BONN
N
0
C
BONN
N
1
B
2
BONN
N
2
B
Ergebnis
wort
zeichen
i
Rückkehradresse
Ergebnis
wort
zeichen
i
Rückkehradresse
BONN
N
0
C
2
BONN
N
1
B
Ergebnis
wort
zeichen
i
Rückkehradresse
2
BONN
N
0
C
2/12
Lösung 2: Backtracking mit Rekursion
Auf einem n∗n Schachbrett soll eine Konfiguration gefunden werden, wo genau eine Dame
in jeder Zeile steht und keine Dame eine andere bedroht. Eine Dame auf einem Feld wird
genau dann nicht bedroht, wenn sich keine Dame in der gleichen Zeile, in der gleichen Spalte
oder diagonal befindet.
Beispiele für n=4, eine Dame ist jeweils durch „1“ markiert:
0
0
1
2
3
2
1
3
Gegenseitige Bedrohungen: keine
1
1
1
0
0
1
2
3
1
1
1
2
1
3
1
1
Die beiden Damen in Zeile 0 bedrohen sich gegenseitig
Die beiden Damen in Spalte 1 bedrohen sich gegenseitig
Die Damen in den Positionen (0,1) und (1,0) bedrohen sich gegenseitig
Die Damen in den Positionen (0,1) und (2,3) bedrohen sich gegenseitig
1
In dem folgenden Java-Programm fehlt noch die Methode plaziere, die Sie im Rahmen
dieser Aufgabe realisieren sollen. Diese Methode soll ab der durch den Parameter gegebenen
Zeile eine Dame pro Zeile bedrohungsfrei plazieren, unter der Voraussetzung, dass in den
vorhergehenden Zeilen bereits je eine Dame bedrohungsfrei plaziert wurde. Die Methode
plaziere muss jeweils alle Felder der Zeile probieren und dann die Methode plaziere
für die nächste Zeile rekursiv aufrufen.
Wichtig: Es genügt, eine Damen-Belegung zu finden
import simpleio.*;
public class Schachbrett {
static int[][] feld;
// Feld für Schachbrett
// 0: auf dem Feldelement befindet sich keine Dame
// 1: auf dem Feldelement befindet sich eine Dame
static int feldgroesse;
/*
* bedroht
* Element
*
* @return
*
*
*/
// Anzahl der Elemente pro Zeile und Spalte
ueberprueft, ob bei der aktuellen Schachbrettbelegung das
feld[zeile][spalte] bedrohungsfrei gesetzt werden kann
true:
feld[zeile][spalte] ist bedroht, kann also
nicht gesetzt werden
false: feld[zeile][spalte] ist nicht bedroht
public static boolean bedroht(int zeile, int spalte){
//
3/12
//
//
//
//
//
Die Methode "bedroht" koennen Sie als gegeben voraussetzen.
Die Implementierung dieser Methode ist für die Aufgabe nicht
relevant und wird daher der Übersicht halber hier nicht
angegeben
}
/**
* plaziere geht davon aus, dass in den Zeilen
* bis einschliesslich "zeile"-1 bereits
* je eine Dame bedrohungsfrei pro Zeile gesetzt ist.
* plaziere versucht, das Feld ab der Zeile "zeile"
* bedrohungsfrei mit je einer Dame
* pro Zeile zu besetzen. Wenn dieses gelingt,
* wird true abgeliefert, andernfalls false.
*
* Der erste Aufruf von plaziere muss sein:
*
* plaziere(0) .
*
Zeilenindex der Zeile, die als
* @param zeile:
naechstes gesetzt werden muss
*
*
* @return true: zeile >= feldgroesse, oder
Besetzung ohne Bedrohung ist gefunden
*
und in feld markiert
*
false: Besetzung nicht moeglich
*
*/
public static boolean plaziere(int zeile) {
//
// Diese Methode ist im Rahmen der Aufgabe zu formulieren
//
}
public static void ausgabe (Ausgabe a){
a.println();
for (int i=0; i<feld.length; i++) {
for (int j=0; j<feld[i].length; j++)
a.print(feld[i][j] + " ");
a.println();
}
}
public static void main(String[] args) {
Ausgabe a = Ausgabe.oeffnen();
4/12
if (args.length==0)
feldgroesse = 8;
else
feldgroesse = Integer.parseInt(args[0]);
feld = new int[feldgroesse][feldgroesse];
a.println();
if (plaziere(0)){
a.println("Eine Loesung ist:");
ausgabe(a);
} else {
a.println("Es gibt keine Loesung !!");
ausgabe(a);
}
}
}
Achten Sie darauf, dass Sie Ihre Lösung mit genügend Kommentar versehen, damit man die
Lösung nachvollziehen kann.
Hinweis zum Aufwand: Es gibt eine Lösung, die einschließlich Kommentar nur aus 28 Zeilen besteht.
(20 Punkte)
Lösung:
public static boolean plaziere(int zeile) {
//
// Ueberpruefung, ob das Ende des Schachbretts erreicht ist
//
if (zeile>=feldgroesse) return true;
//
// alle Elemente der Zeile, die nicht bedroht sind, probieren
//
for (int spalte=0; spalte<feldgroesse; spalte++){
if(!bedroht(zeile,spalte)) {
//
// Element setzen und naechste Zeile plazieren
//
feld[zeile][spalte] = 1;
if (plaziere(zeile+1)) return true;
//
// kein Erfolg, d.h. Element zuruecksetzen
// und naechste Spalte probieren
//
feld[zeile][spalte] = 0;
5/12
}
}
//
// alle Spalten haben zu keinem Erfolg gefuehrt, d.h. es gibt
// keine bedrohungsfreie Belegung der aktuellen Zeile
//
return false;
}
Lösung 3: Hashtabelle
Gegeben sei die unten abgebildete Hashtabelle, bei der die Schlüssel mit der Hashfunktion
h(k) = Querprodukt(k) M OD 11 bestimmt werden.
Für eine Zahl z = z1 z2 ...zn wird hierbei definiert: Querprodukt(z1 z2 ...zn ) = z1 ∗ z2 ∗ ... ∗ zn
Als Kollisionsstrategie wird lineares Sondieren angewandt, d. h. hi (k) = (hi−1 (k)+1) M OD 11.
a) Fügen Sie die Werte 14, 11, 20 in der angegebenen Reihenfolge in die folgende Hashtabelle ein und geben Sie dabei die Anzahl der aufgetretenen Kollisionen an.
0
1
2
30
26
38
3
4
5
6
7
8
9
18
33
10
(6 Punkte)
b) Welches Problem tritt beim Löschen auf, wenn beim vorausgegangenen Einfügen Kollisionen aufgetreten sind? Wie kann man das Problem lösen ?
(3 Punkte)
Lösung:
a) Einfügen der Werte 14, 11, 20 in der angegebenen Reihenfolge ergibt:
• Einfügen der 14 mit h(14) = 4 M OD 11 = 4:
0
1
2
30
26
38
3
4
5
6
7
14
8
9
18
33
10
• Einfügen der 11 mit h(11) = 1 M OD 11 = 1
(Kollision → h1 (11) = (1 + 1) M OD 11 = 2,
Kollision → h2 (11) = (2 + 1) M OD 11 = 3):
0
1
2
3
4
30
26
38
11
14
5
6
7
8
9
18
33
10
• Einfügen der 20 mit h(20) = 0 M OD 11 = 0
(Kollision → h1 (20) = (0 + 1) M OD 11 = 1,
Kollision → h2 (20) = (1 + 1) M OD 11 = 2,
Kollision → h3 (20) = (2 + 1) M OD 11 = 3,
Kollision → h4 (20) = (3 + 1) M OD 11 = 4,
Kollision → h5 (20) = (4 + 1) M OD 11 = 5):
0
1
2
3
4
5
30
26
38
11
14
20
6
7
8
9
18
33
6/12
10
b) Einträge müssen als gelöscht markiert werden, da sonst mit Kollisionen eingetragene
Werte nicht mehr wiedergefunden werden, die Sondierkette ist unterbrochen. Alternativ muss die Tabelle so umorganisiert werden, dass die Sondierketten nicht unterbrochen werden
Lösung 4: Sortierverfahren
a) Es seien eine sortierte Liste L und eine unsortierte Liste L0 gegeben, wobei L sehr
lang im Vergleich zu L0 ist. Eine Möglichkeit, die aus den Elementen von L und L0
bestehende Liste zu sortieren, besteht darin, L0 ans Ende von L anzufügen und die so
entstandene Liste zu sortieren. Wählen Sie aus den Verfahren Sortieren durch direktes
Aussuchen, Sortieren durch direktes Einfügen und Heapsort dasjenige aus, welches
diese Aufgabe am effizientesten löst und begründen Sie Ihre Antwort
(8 Punkte)
b) Welche Laufzeit hat Quicksort im Mittel und im schlechtesten Fall ?
(6 Punkte)
c) Wieviele Schlüsselvergleiche benötigt jedes allgemeine Sortierverfahren zum Sortieren
von N verschiedenen Schlüsseln sowohl im Mittel als auch im schlechtesten Fall mindestens ?
(3 Punkte)
Lösung:
a) Von den Verfahren Sortieren durch direktes Aussuchen, Sortieren durch direktes Einfügen und Heapsort ist Sortieren durch direktes Einfügen das Verfahren, das in diesem
Fall die gute Vorsortierung am besten ausnützt. Die Elemente aus der Liste L werden
nur einmal durchlaufen, ohne dass Vertauschungen notwendig sind. Die wenigen Elemente von L0 müssen anschließend in den L-Teil durch Einfügen einsortiert werden.
Die anderen Verfahren nutzen die Vorsortierung überhaupt nicht aus.
b) im Mittel: O(N ∗ log(N )), im schlechtesten Fall: O(N 2 )
c) Ω(N ∗ log(N ))
Lösung 5: Minimale Spannende Bäume
Gegeben sei der folgende gewichtete, ungerichtete Graph:
7/12
H
A
8
9
11
D
5
2
E
B
1
6
3
F
Führen Sie den Algorithmus von Kruskal zur Berechnung eines minimalen Spannenden
Baumes durch. Geben Sie in jedem Schritt die betrachtete Kante und die einzelnen Teilbäume
an.
(12 Punkte)
Lösung:
H
A
8
9
11
D
5
2
E
B
1
6
3
F
1 D
F
wählen:
8/12
H
A
8
9
11
D
5
2
E
B
1
6
3
F
2 E
B
wählen:
H
A
8
9
11
D
5
2
E
B
1
6
3
F
3 B
F
wählen:
9/12
H
A
8
9
11
D
5
2
E
B
1
6
3
F
5 D
E
verwerfen
6 E
F
verwerfen
8 D
H
wählen:
H
A
8
9
11
D
5
2
E
B
1
6
3
F
9 H
E
verwerfen
11 B
A
wählen:
10/12
H
A
8
9
11
D
5
2
E
B
1
6
3
F
Lösung 6: Kürzeste Wege
Gegeben sei folgender gewichteter Graph G:
a) Wenden Sie den Algorithmus von Dijkstra für kürzeste Wege auf den Graphen an. Der
Startknoten sei die 1. Geben Sie die Zwischenschritte wie in der Vorlesung in einer
Tabelle der folgenden Art an:
Nr.
.
.
.
gewählt
Entfernung Vorgänger
.
.
.
.
.
.
Nr.
.
.
.
Randknoten
Entfernung Vorgänger
.
.
.
.
.
.
(12 Punkte)
b) Fügen Sie eine ungerichtete Kante mit dem Gewicht −1 von Knoten 4 zu Knoten 3 in
G ein. Entnehmen Sie jetzt G den kürzesten Weg von Knoten 1 zu Knoten 3, ohne den
Algorithmus aus a) zu verwenden.
(2 Punkte)
Lösung:
11/12
a)
Nr.
1
gewählt
Entfernung Vorgänger
0
1
4
5
1
3
10
1
5
2
15
20
4
3
Nr.
2
3
4
2
3
5
2
5
2
Randknoten
Entfernung Vorgänger
30
1
10
1
5
1
30
1
10
1
15
4
20
3
15
4
20
3
b)
-1
Die ungerichtete Kante mit negativem Gewicht kann unendlich oft hin und her durchlaufen werden. Jedesmal reduziert sich die Gesamtlänge des Weges um eins. Die korrekte Antwort ist deshalb -∞.
12/12
Herunterladen