1 2 4 3 5 6 a) Geben Sie an, wie der Graph durch ei

Werbung
Algorithmik II
Aufgabe 20
8. Übung
Graphen
Gegeben sei der folgende Graph:
3
A
A
A
4 A
@ A
@A
@
AU
R
@
-2
- 5 1
6
a) Geben Sie an, wie der Graph durch eine Adjazenzmatrix und durch Adjazenzlisten gespeichert wird.
Lösungsvorschlag:
Ein gerichteter Graph G = (V, E) ist gegeben durch zwei Mengen V und E mit E ⊆ V × V .
(a) V : Knotenmenge, n =| V |
(b) E: Kantenmenge, e =| E |
• Adjazenzmatrix:
Ein Graph G wird in einer n × n–Matrix gespeichert, wobei ein Matrixelement ai,j
(1 ≤ i, j ≤ n) eine 1 enthält, falls G eine Kante vom Knoten i zum Knoten j enthält,
ansonsten ist das Matrixelement mit einer 0 besetzt.
1
0
0
0
0
0
0
1
2
3
4
5
6
2
1
0
0
0
0
0
3
0
1
0
0
0
0
4
0
1
0
0
0
0
5
0
1
1
1
0
1
6
0
0
0
0
0
0
• Adjazenzlisten:
Jedem Knoten i des Graphen G wird eine lineare Liste zugeordnet. Diese Liste enthält
für jeden Knoten j, der mit i durch eine Kante direkt verbunden ist (d. h. (i, j) ∈ E),
einen Eintrag.
1
?
2
2
?
4
3
?
5
4
?
5
5
6
?
5
?
3
?
5
16. Juni - 20. Juni 2003
-1-
Sommersemester 2003
Algorithmik II
8. Übung
b) Bestimmen Sie für den Graphen eine topologische Sortierung.
Lösungsvorschlag:
(a) Topologisches Sortieren: Erster Entwurf
Eine topologische Sortierung ordnet jedem Knoten des Graphen eine natürliche Zahl
zwischen 1 und n zu, so daß für jede Kante der Quellknoten eine kleinere Zahl als der
Zielknoten hat.
Der Algorithmus ordnet zunächst jedem Knoten die minimal mögliche Ordnungszahl“
”
(1) zu. Daran anschließend werden in einer Schleife über alle Kanten die Ordnungs”
zahlen“ erhöht, solange noch Unstimmigkeiten“ bestehen, d. h. solange es eine Kante
”
gibt, deren Zielknoten eine kleinere Zahl als der Quellknoten v hat.
1
Initialisierung 1
v=1
v=2
v=3
v=4
v=5
v=6
v=1
..
.
2
1
2
3
1
4
1
5
1
3
3
3
4
6
1
v=6
Der zweite Schleifendurchlauf über alle Kanten führt zu keiner weiteren Änderung.
(b) Topologisches Sortieren: Schnelleres Verfahren
Mit indegree(v) (Eingangsgrad(v)) bezeichnet man die Anzahl der Kanten, die den
Knoten v als Zielknoten haben:
indegree(v) =| {v 0 | (v 0 , v) ∈ E} |
Der Algorithmus betrachtet zunächst einen Knoten v mit Eingangsgrad 0 und ordnet
diesem die Ordnungszahl“ 1 zu. Daran anschließend wird der Knoten v entfernt“ und
”
”
der Algorithmus arbeitet nun mit einen um einen Knoten verkleinerten Graphen und der
Ordnungszahl“ 2 weiter, usw. Das Entfernen“ eines Knotens wird dadurch realisiert,
”
”
daß die Eingangsgrade seiner direkten Nachbarn um den Wert 1 verringert werden. Um
einen Knoten mit Eingangsgrad 0 schnell zu finden, wird eine zusätzliche Menge zeroin
eingeführt, die all die Knoten mit aktuellem Eingangsgrad 0 enthält. Der Algorithmus
vergibt jede Zahl zwischen 1 und n genau einmal. Dies hat zur Folge, daß jeder Knoten
eine andere Ordnungszahl“ hat. Wird eine Ordnungszahl“ vergeben, die größer als die
”
”
Knotenanzahl n ist, so ist eine topologische Sortierung des Graphen unmöglich.
• Initialisierung:
v
1 2 3 4
–
indegree(v) 0 1 1 1
– zeroin = {1, 6}, i = 0
• Schleifendurchlauf mit v = 1:
16. Juni - 20. Juni 2003
5 6
4 0
-2-
Sommersemester 2003
Algorithmik II
•
•
•
•
•
8. Übung
– zeroin = {6}, i = 1, ord(1) = 1
– (1, 2) ∈ E =⇒ indegree(2) = 0, zeroin ={2, 6}
Schleifendurchlauf mit v = 2:
– zeroin = {6}, i = 2, ord(2) = 2
– (2, 3) ∈ E =⇒ indegree(3) = 0, zeroin ={3, 6}
– (2, 4) ∈ E =⇒ indegree(4) = 0, zeroin ={4, 3, 6}
– (2, 5) ∈ E =⇒ indegree(5) = 3
Schleifendurchlauf mit v = 4:
– zeroin = {3, 6}, i = 3, ord(4) = 3
– (4, 5) ∈ E =⇒ indegree(5) = 2
Schleifendurchlauf mit v = 3:
– zeroin = {6}, i = 4, ord(3) = 4
– (3, 5) ∈ E =⇒ indegree(5) = 1
Schleifendurchlauf mit v = 6:
– zeroin = ∅, i = 5, ord(6) = 5
– (6, 5) ∈ E =⇒ indegree(5) = 0, zeroin ={5}
Schleifendurchlauf mit v = 5:
– zeroin = ∅, i = 6, ord(5) = 6
c) Bestimmen Sie mit Hilfe des Algorithmus zum systematischen Durchlaufen eines Graphen
alle Knoten, die von Knoten 1 aus erreichbar sind. Als Auswahlstrategie soll LIFO verwendet
werden.
Lösungsvorschlag:
Der Algorithmus dient dazu, die Menge S aller von einem Knoten aus erreichbaren Knoten
zu bestimmen. Hierbei soll keine Kante mehrfach durchlaufen werden. Um noch nicht besuchte Kanten effizient bestimmen zu können, wird neben der Menge S eine weitere Menge
R eingeführt. R enthält all diejenigen Knoten aus S, von denen noch nicht besuchte Kanten
ausgehen. R bezeichnet man auch als den Rand von S. Für jeden Knoten v aus R gibt es eine
Liste p[v], die die noch nicht besuchten Kanten enthält, die von v ausgehen. Ist der Graph mit
Hilfe von Adjazenzlisten gespeichert, so kann p[v] einfach mit adj liste[v] initialisiert werden. Wird eine Kante bearbeitet, deren Zielknoten noch nicht in S ist, so wird dieser Knoten
sowohl in S als auch in R eingetragen. LIFO–Auswahlstrategie bedeutet, daß als nächstes immer eine Kante des Knotens betrachtet wird, der zuletzt in die Menge R eingetragen wurde.
Die Kanten eines Knotens v werden in der Reihenfolge bearbeitet, die durch p[v] gegeben ist.
• Initialisierung:
– S = {1}, R = {1}
– p[1] = adj liste[1]
• Schleifendurchlauf für v = 1:
– (1, 2) ist die nächste Kante von p[1]
– setze p[1] ein Element weiter, d. h. auf den Abschlußzeiger von adj liste[1]
– S = {2, 1}, R = {2, 1}, p[2] = adj liste[2]
16. Juni - 20. Juni 2003
-3-
Sommersemester 2003
Algorithmik II
8. Übung
• Schleifendurchlauf für v = 2:
– (2, 3) ist die nächste Kante von p[2]
– setze p[2] ein Element weiter, d. h. auf den Knoten 3
– S = {3, 2, 1}, R = {3, 2, 1}, p[3] = adj liste[3]
• Schleifendurchlauf für v = 3:
– (3, 5) ist die nächste Kante von p[3]
– setze p[3] ein Element weiter, d. h. auf den Abschlußzeiger von adj liste[3]
– S = {5, 3, 2, 1}, R = {5, 3, 2, 1}, p[5] = adj liste[5]
• Schleifendurchlauf für v = 5:
– p[5] ist undefiniert =⇒ R = {3, 2, 1}
• Schleifendurchlauf für v = 3:
– p[3] ist undefiniert =⇒ R = {2, 1}
• Schleifendurchlauf für v = 2:
– (2, 4) ist die nächste Kante von p[2]
– setze p[2] ein Element weiter, d. h. auf den Knoten 4
– S = {4, 5, 3, 2, 1}, R = {4, 2, 1}, p[4] = adj liste[4]
• Schleifendurchlauf für v = 4:
– (4, 5) ist die nächste Kante von p[5]
– setze p[4] ein Element weiter, d. h. auf den Abschlußzeiger von adj liste[4]
– R und S ändern sich nicht, da der Knoten 5 bereits in S vorhanden ist
• Schleifendurchlauf für v = 4:
– p[4] ist undefiniert =⇒ R = {2, 1}
• Schleifendurchlauf für v = 2:
– (2, 5) ist die nächste Kante von p[2]
– setze p[2] ein Element weiter, d. h. auf den Abschlußzeiger von adj liste[2]
– R und S ändern sich nicht, da der Knoten 5 bereits in S vorhanden ist
• Schleifendurchlauf für v = 2:
– p[2] ist undefiniert =⇒ R = {1}
• Schleifendurchlauf für v = 1:
– p[1] ist undefiniert =⇒ R = ∅
Vom Knoten 1 sind somit die Knoten 4, 5, 3, 2 und 1 erreichbar.
3
1
16. Juni - 20. Juni 2003
-
A
A
A
4 A
A
A
AU
5
2
-4-
Sommersemester 2003
Algorithmik II
8. Übung
Rechnerübung 15
Graphen
a) Schreiben Sie eine Klasse AdjList, die es Ihnen ermöglicht, einen gerichteten Graphen
in einer Adjazenzliste zu speichern sowie eine Klasse AdjMatrix, mit der ein gerichteter
Graph durch eine Adjazenzmatrix gespeichert werden kann. Dem Kontruktor beider Klassen
soll jeweils die Anzahl der Knoten des Graphen als Parameter übergeben werden. Die Speicherung der von den einzelnen Knoten ausgehenden Kanten soll in der Klasse AdjList mit
Hilfe einer verketteten Liste realisiert werden.
Weiterhin sollen beide Graphen eine Methode insertEdge(int from, int to) zur
Verfügung stellen, mit der Sie eine Kante vom Knoten from zum Knoten to einfügen
können. Dabei soll die Benennung der Knoten bei 1 beginnen.
Implementieren Sie auch die Methode toString() für beide Klassen.
Lösungsvorschlag:
class AdjListElement {
private int nodeNumber;
private AdjListElement next;
public AdjListElement() {
next = null;
nodeNumber = -1;
}
public AdjListElement(int node) {
next = null;
nodeNumber = node;
}
public int getNodeNumber() { return nodeNumber; }
public void setNodeNumber(int number) { nodeNumber = number; }
public AdjListElement getNext() { return next; }
public void setNext(AdjListElement next) { this.next = next; }
}
public class AdjList {
public AdjListElement nodes[];
public AdjList(int numNodes) {
nodes = new AdjListElement[numNodes];
}
public String toString() {
String s = "";
for(int n=0; n < nodes.length; n++) {
s += (n + 1) + ": ";
16. Juni - 20. Juni 2003
-5-
Sommersemester 2003
Algorithmik II
8. Übung
if(nodes[n] == null) {
s += "*\n";
continue;
}
AdjListElement currNode = nodes[n];
do {
s += currNode.getNodeNumber() + " - ";
currNode = currNode.getNext();
} while(currNode != null);
s += "*\n";
}
return s;
}
// neue Kante einfuegen
public void insertEdge(int from, int to) {
if(from < 1 || from > nodes.length
|| to < 1 || to > nodes.length) {
System.out.println("Angegebener Knoten existiert nicht!");
return;
}
AdjListElement currNode = nodes[from-1];
// erste Kante
if(currNode == null) {
nodes[from-1] = new AdjListElement(to);
return;
}
// neue Kante am Ende anfuegen
while(currNode.getNext() != null) {
currNode = currNode.getNext();
}
currNode.setNext(new AdjListElement(to));
}
}
class AdjMatrix {
public boolean matrix[][];
final static boolean EDGE = true;
final static boolean NO_EDGE = false;
16. Juni - 20. Juni 2003
-6-
Sommersemester 2003
Algorithmik II
8. Übung
AdjMatrix(int dimension) {
matrix = new boolean[dimension][dimension];
}
public String toString() {
String s = "";
for(int n=0; n < matrix.length; n++) {
s += (n + 1) + ": ";
for(int edgeTo=0; edgeTo < matrix.length; edgeTo++) {
if(matrix[n][edgeTo] == EDGE) {
s += (edgeTo + 1) + " - ";
}
}
s += "*\n";
}
return s;
}
// neue Kante einfuegen
public void insertEdge(int from, int to) {
if(from < 1 || from > matrix.length
|| to < 1 || to > matrix.length) {
System.out.println("Angegebener Knoten existiert nicht!");
return;
}
matrix[from -1][to -1] = EDGE;
}
}
b) * Schreiben Sie für die Klasse AdjMatrix eine Methode AdjMatrixToAdjList(),
die ein der Matrizendarstellung entsprechendes AdjList-Objekt zurückliefert sowie für die
Klasse AdjList eine Methode AdjListToAdjMatrix().
Lösungsvorschlag:
public AdjMatrixToAdjList() {
AdjList graph = new AdjList(matrix.length);
for(int source = 0; source < matrix.length; source++) {
for(int goal = 0; goal < matrix.length; goal++) {
if(matrix[source][goal] == EDGE) {
graph.insert(source + 1, goal + 1);
}
}
}
16. Juni - 20. Juni 2003
-7-
Sommersemester 2003
Algorithmik II
8. Übung
return graph;
}
public AdjListToAdjMatrix() {
AdjMatrix graph = new AdjMatrix(nodes.length);
for(int n = 0; n < nodes.length; n++) {
AdjListElement current = nodes[n];
while(current != null) {
graph.insert(n + 1, current.getNodeNumber());
current = current.getNext();
}
}
return graph;
}
c) Schreiben Sie für die Klasse AdjList eine Methode topologicalSort(), die die topologische Sortierung des Graphen unter Verwendung des in der Vorlesung vorgestellten verbesserten Algorithmus zum topologischen Sortieren ausgibt. Weiterhin soll die Methode ausgeben, ob der Graph zyklenfrei ist.
Testen Sie Ihre Implementierung anhand einiger Beispiele aus der Vorlesung.
Lösungsvorschlag:
public class AdjList {
...
public void topologicalSort() {
int i, indegree[], ord[];
ArrayList zeroin = new ArrayList();
indegree = new int[nodes.length];
ord = new int [nodes.length];
for(i=0; i < nodes.length; i++)
indegree[i] = 0;
// Zunaechst den indegree jedes Knotens ermitteln
for(i=0; i < nodes.length; i++) {
AdjListElement currNode = nodes[i];
while(currNode != null) {
indegree[currNode.getNodeNumber() -1]++;
currNode = currNode.getNext();
}
}
// jetzt die Menge zeroin ermitteln
for(i=0; i < nodes.length; i++) {
16. Juni - 20. Juni 2003
-8-
Sommersemester 2003
Algorithmik II
8. Übung
if(indegree[i] == 0)
zeroin.add(new Integer(i));
}
i=0;
// Jetzt ueber alle Elemente von zeroin gehen
while(!zeroin.isEmpty()) {
// Waehle das erste Element v aus zeroin
Integer currZeroinNode = (Integer) zeroin.get(0);
int zeroinNode = currZeroinNode.intValue();
// nimm v aus zeroin heraus
zeroin.remove(currZeroinNode);
ord[zeroinNode] = ++i;
AdjListElement currNode = nodes[zeroinNode];
while(currNode != null) {
indegree[currNode.getNodeNumber() -1]--;
if(indegree[currNode.getNodeNumber() -1] == 0)
zeroin.add(new Integer(currNode.getNodeNumber() -1));
currNode = currNode.getNext();
}
}
if(i >= nodes.length)
System.out.println("Der Graph ist zyklenfrei!");
else {
System.out.println("Der Graph ist nicht zyklenfrei!");
return;
}
// noch sortieren
int sortedList[] = new int[nodes.length];
for(i=0; i < nodes.length; i++) {
// Bestimmen des Knotens mit dem naechstgroessten ord-Wert
for(int n=0; n < nodes.length; n++)
if(ord[n] == i+1) {
sortedList[i] = n;
break;
}
}
// Ausgabe:
16. Juni - 20. Juni 2003
-9-
Sommersemester 2003
Algorithmik II
8. Übung
System.out.println("Topologische Sortierung:");
for(int n=0; n < nodes.length; n++) {
System.out.println(sortedList[n] +1);
}
}
}
16. Juni - 20. Juni 2003
- 10 -
Sommersemester 2003
Herunterladen