Übung: Algorithmen und Datenstrukturen SS 2007

Werbung
Übung: Algorithmen und Datenstrukturen
SS 2007
Prof. Lengauer
Sven Apel, Michael Claÿen, Christoph Zengler, Christof König
Blatt 9
Votierung in der Woche vom 02.06.0706.06.07 Aufgabe 26
Vergleich von Suchverfahren
Im Skript nden sich auf Seite 68 und 69 zwei Übersichtstabellen zu den Laufzeiten einzelner Operationen. Die folgenden Aussagen sind diesen beiden Tabellen
entnommen. Begründen Sie diese jeweils. Falls es sich um einen worst case handelt, reicht zur Begründung die Angabe eines Beispiels.
(a) Im average case benötigt eine erfolgreiche Suche in einer nicht sortierten
Liste (n + 1)/2 Schritte.
Sei n die Länge der Liste, im Erfolgsfall wird die Suche spätestens beim letzten Element fündig. Die Wahrscheinlichkeit dafür an der
i-ten Stelle (1 ≤ i ≤ n) fündig zu werden ist 1/n. Damit ergibt sich der
folgende Erwartungswert:
Lösung:
E(n) =
1
1
1
· 1 + · 2 + ··· + · n
n
n
n
Dies läÿt sich zu
E(n) =
1
1 n(n + 1)
n+1
· (1 + 2 + · · · + n) = ·
=
n
n
2
2
vereinfachen.
(b) Im failure case benötigt die Suche in einer nicht sortierten Liste im Durchschnitt n + 1 Schritte.
Im Fehlerfall muss die Liste komplett bis zum Ende abgesucht
werden. Damit sind n + 1 Schritte nötig.
Lösung:
1
(c) Im failure case benötigt die Suche in einer sortierten Liste im Durchschnitt
(n + 2)/2 Schritte.
Sei x der zu suchende Wert und i die Position desjenigen Elements in der sortierten Liste, das als erstes gröÿer als x ist. Im Extremfall
ist x gröÿer als alle Elemente in der Liste, dann sei i = n + 1. Jede der
Positionen von 1 ≤ i ≤ n+1 ist wiederum gleichwahrscheinlich. Und somit
ergibt sich wieder:
Lösung:
E(n) =
1
1
(n + 1)(n + 2)
n+2
· (1 + 2 + · · · + (n + 1)) =
·
=
n+1
n+1
2
2
(d) In einem binären Suchbaum ist der worst case für das Finden eines Elements n Schritte.
Ein binärer Suchbaum kann zu einem Pfad ausarten. Falls das
gesuchte Element das Blatt des Pfades ist, benötigt man n Schritte.
Lösung:
(e) Sei n die Anzahl der Werte in einer Hash-Tabelle mit m Slots. Warum
braucht man zum Finden des Minimums/Maximums m Schritte? Handelt
es sich dabei um eine worst oder best case Abschätzung?
Es müssen jeweils alle Slots der Hash-Tabelle durchsucht werden, um eindeutig das Minimum bestimmen zu können. Also sowohl worst
als auch best case Abschätzung. Man hat vielleicht schon eher das Minimum gefunden, kann sich aber erst am Ende sicher sein, dass es tatsächlich das Minimum ist. Wenn man das Löschen von Einträgen vernachlässigt, kann man das Minimum/Maximum auch jeweils in einem Schritt
berechnen. Dazu benötigt man nur einen zusätzlichen Verweis in der HashTabelle.
Lösung:
(f) Was ist mit left-height bzw. right-height fr die Laufzeit zum Finden des
Minimums/Maximums in einem Suchbaum gemeint? Wie sieht damit die
worst, average und best case Laufzeit genau aus?
Bei left-height handelt es sich um die Höhe des linkesten Pfades
im Suchbaum. Analog für right-height. Damit ergibt sich für den worst
case eine Abschätzung von n Schritten (Ausartung zum Pfad), für den
average case eine logarithmische Abschätzung und für den best case eine
Operation, die in einem Schritt ausgeführt werden kann.
Lösung:
Aufgabe 27
Hashing
Gegeben ist folgender Code einer Klasse Point:
public class Point {
2
public int x;
public int y;
public Point(int x, int y) {
this.x = x;
this.y = y;
}
public String toString() {
return "(" + x + "/" + y + ")";
}
}
public boolean equals(Object o) {
if (o instanceof Point) {
Point p = (Point) o;
return p.x == this.x && p.y == this.y;
}
return false;
}
Diese Punkte sollen in ein Array mittels eines Hashing-Verfahrens einsortiert
werden. Dazu gibt es die Klasse PointArray. Der Konstruktor dieser Klasse legt
ein neues PointArray einer gegebenen Gröÿe an. Die Funktion getHash(Point p)
liefert zu einem Punkt p den entsprechenden Hashwert. Die Methode printArray()
gibt das PointArray aus.
Das Codefragment hierzu sieht wie folgt aus:
public class PointArray{
private Point[] points;
public PointArray(int length) {
points = new Point[length];
}
private int getHash(Point p) {
return (p.x + p.y) % points.length;
}
public void printArray() {
for (int i=0; i<points.length-1; i++) {
System.out.print(points[i] + " - ");
}
System.out.print(points[points.length-1] + "\n");
}
3
}
a) Implementieren Sie eine Methode insertPoint(Point p), die einen gegebenen Punkt p in das Array einfügt. Die Position des Punktes im Array soll
dabei über die Hashfunktion ermittelt werden. Bei einer Kollision sollen Sie die
Strategie des linear chaining / probing benutzen. Behandeln Sie auch den Fall,
dass das Array voll ist.
b) Implementieren Sie eine Methode getPoint(Point p), die einen gegebenen
Punkt p im Array sucht. Sie soll den Punkt zurückgeben, falls sie ihn gefunden
hat. Findet sie den Punkt nicht, soll sie null zurückgeben. Die Funktion soll
dabei natürlich nur so weit wie nötig suchen, und nicht jedesmal das ganze Array durchlaufen.
c) Testen Sie Ihre Methoden mit der folgenden Testklasse und verizieren Sie
die Ergebnisse:
public class Test {
public static void main(String[] args) {
PointArray points = new PointArray(10);
Point
Point
Point
Point
Point
Point
Point
p1
p2
p3
p4
p5
p6
p7
=
=
=
=
=
=
=
new
new
new
new
new
new
new
Point(4,4);
Point(5,3);
Point(7,8);
Point(9,1);
Point(10,11);
Point(14,11);
Point(1,1);
points.insertPoint(p1);
points.insertPoint(p2);
points.insertPoint(p3);
points.insertPoint(p4);
points.insertPoint(p5);
points.insertPoint(p6);
points.insertPoint(p7);
System.out.println("Array: ");
points.printArray();
System.out.println("\nSearches: ");
System.out.println("4,4: " + points.getPoint(new Point(4,4)));
System.out.println("14,11: " + points.getPoint(new Point(14,11)));
System.out.println("3,0: " + points.getPoint(new Point(3,0)));
System.out.println("1,1: " + points.getPoint(new Point(1,1)));
System.out.println("8,0: " + points.getPoint(new Point(8,0)));
4
}
}
Anmerkung: Den Code Der Klassen Point und Test nden Sie im Stud.IP und
auf der Vorlesungs-Hompage. Ebenso das Codefragment der Klasse PointArray.
Lösung: a)
public void insertPoint(Point p) {
assert numOfElements < points.length : "There's no place left in the array";
int place = getHash(p);
// Array[place] is empty, so insert Point here
if (points[place] == null) {
points[place] = p;
numOfElements++;
}
// Search for the next free position
} else {
for (int i=0; i<points.length; i++) {
if (points[(place + 1 + i) % points.length] == null) {
points[(place + 1 + i) % points.length] = p;
numOfElements++;
return;
}
}
}
b)
public Point getPoint(Point p) {
int place = getHash(p);
// Place is empty, so return null
if (points[place] == null) {
return null;
}
// Point was found at place, so return it
if (points[place].equals(p)) {
return p;
// Search in the array until the point or a null-entry is found
} else {
for (int i=0; i<points.length; i++) {
Point temp = points[(place + 1 + i) % points.length];
5
if (temp == null) {
return null;
} else if (temp.equals(p)) {
return temp;
}
}
}
}
return null;
c)
Array:
(9/1) - (10/11) - (1/1) - null - null - (7/8) - (14/11) - null - (4/4) - (5/3)
Searches:
4,4: (4/4)
14,11: (14/11)
3,0: null
1,1: (1/1)
8,0: null
Aufgabe 28
Charakterisierungen von Bäumen
Sei G = (V, E) ein einfacher ungerichteter Graph mit n Knoten und m Kanten.
Zeigen Sie, dass folgende Aussagen äquivalent sind:
1. G ist zusammenhängend und m = n − 1.
2. G ist kreisfrei und m = n − 1.
3. Jede weitere Kante erzeugt einen (den ersten) Kreis.
4. Zwischen je zwei Knoten u, v gibt es genau einen einfachen Weg
Lösung:
Wir zeigen die Äquivalenz der vier Aussagen, indem wir 1 ⇒ 2 ⇒
3 ⇒ 4 ⇒ 1 zeigen.
1 ⇒ 2 : Sei G = (V, E) ein zusammenhängender einfacher Graph mit |E| =
|V | − 1. Es ist zu zeigen, dass G kreisfrei ist. Annahme: G enthalte einen Kreis.
Sei Gz = (Vz , Ez ) ein derartiger Kreis. Es gilt |Vz | = |Ez |. Deniere die Graphen Gi = (Vi , Ei ) für |Vz | ≤ i ≤ |V | induktiv folgendermaÿen: Der Basisfall
ist G|Vz | = Gz . Für alle |Vz | < i ≤ |V | wähle ein vi ∈ V \ Vi−1 aus, so dass ein
vi0 ∈ Vi−1 existiert mit ei = {vi , vi0 } ∈ E . Einen derartigen Knoten vi gibt es
immer, da G zusammenhängend ist. Deniere Gi := (Vi−1 ∪ {vi }, Ei−1 ∪ {ei })
für |Vz | < i ≤ |V |. Dann gilt für alle |Vz | ≤ i ≤ |V | : |Vi | = |Ei |. Auÿerdem ist
G|V | ein Teilgraph von G, wobei V|V | = V ist und E|V | ⊆ E . Dies widerspricht
aber |E| = |V | − 1.
6
2 ⇒ 3 : Sei G = (V, E) ein
kreisfreier Graph mit |E| = |V | − 1. Es ist zu
zeigen, dass für alle e ∈ V2 \ E : G0 := (V, E ∪ e) enthält einen (ersten) Kreis.
Annahme: es existiert ein e ∈ V2 \E : G0 := (V, E ∪e) keinen Kreis enthält. Für
G0 gilt |V | = |E| + 1 = |E ∪ e|. Solange G0 Knoten vom Grad 1 enthält, entferne
diese. Man erhält den Graph G00 = (V 00 , E 00 ), so dass |V 00 | = |E 00 | und V 00 6= ∅.
Damit enthält G00 nur noch Knoten mit Grad mindestens 2. Sei v ∈ V 00 beliebig.
Starte eine Tiefensuche bei v bis die erste Rückkante gefunden wurde. Da jeder
Knoten mindestens Grad zwei hat, kann man jeden Knoten wieder verlassen. Da
es nicht unendlich viele Knoten gibt, wird man irgendwann bei einem schon besuchten Knoten nocheinmal landen. Diese Rückkante schlieÿt einen Kreis. Dies
widerspricht der Annahme.
3 ⇒ 4 : Sei G ein Graph, so dass jede weitere Kanten einen ersten Kreis erzeugt.
Zu zeigen: zwischen je zwei Knoten u, v ∈ V gibt es genau einen einfachen Weg.
Annahme: Sei u, v ∈ V , so dass es zwei einfache Wege W1 = [u, . . . , v] und
W2 = [u, . . . , v] zwischen u und v gibt. Sei W das gemeinsame Präx der Wege
W1 und W2 und x der letzte Knoten des gemeinsamen Präx. Ersetze in beiden
Wegen das Präx durch x. Suche anschlieÿend in beiden Wegen den nächsten
gemeinsamen Knoten y . Damit gibt es zwei disjunkte einfache Wege zwischen x
und y und somit einen Kreis. Widerspruch zur Voraussetzung.
4 ⇒ 1 : Sei G = (V, E) ein Graph, so dass es zwischen je zwei Knoten u, v ∈ V
genau einen einfachen Weg gibt. Damit ist G zusammenhängend. Es bleibt
noch zu zeigen, dass |E| = |V | − 1 ist. Induktion über |V |. Die beiden Basisfälle |V | = 1, 2 sind klar. Induktionsschritt: Sei e = {u, v} ∈ E beliebig.
Dann handelt es sich hierbei um den einen einfachen Weg zwischen u und v .
Entfernt man e zerfällt der Graph G in zwei Teilgraphen G1 = (V1 , E1 ) und
G2 = (V2 , E2 ), so dass V = V1 ∪ V2 und E = E1 ∪ E2 ∪ {e} und somit
|V | = |V1 | + |V2 | und |E| = |E1 | + |E2 | + 1. Damit erhält man nach Induktionsannahme: |E| = |V1 | − 1 + |V2 | − 1 + 1 = |V | − 1. Damit ist die Behauptung
bewiesen.
7
Herunterladen